/**********************************************************************
Copyright (c) 2008 Andy Jefferson and others. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Contributors:
2008 Eric Sultan - some handling for 1-1, 1-N
...
**********************************************************************/
package org.datanucleus.store.xml.fieldmanager;
import java.util.Collection;
import javax.xml.bind.JAXBException;
import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.exceptions.NucleusException;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.FieldRole;
import org.datanucleus.metadata.Relation;
import org.datanucleus.store.ExecutionContext;
import org.datanucleus.store.ObjectProvider;
import org.datanucleus.store.fieldmanager.AbstractFieldManager;
import org.datanucleus.store.xml.XMLUtils;
import org.datanucleus.store.xml.binder.JAXBRuntimeBinder;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* FieldManager for retrieving field values from XML results.
*/
public class FetchFieldManager extends AbstractFieldManager
{
ObjectProvider sm;
/** Unmarshalled object. */
Object value;
/** Document to use for reading the object. */
Document doc;
/** Node representing the object having its fields fetched. */
Node node;
public FetchFieldManager(ObjectProvider sm, Document doc)
{
this.sm = sm;
this.doc = doc;
node = XMLUtils.findNode(doc, sm);
try
{
value = JAXBRuntimeBinder.unmarshall(sm.getObject().getClass(), node, sm.getExecutionContext().getMetaDataManager(),
sm.getExecutionContext().getClassLoaderResolver());
}
catch (JAXBException e)
{
e.printStackTrace();
}
}
public String fetchStringField(int fieldNumber)
{
sm.copyFieldsFromObject(value, new int[]{fieldNumber});
return (String) sm.provideField(fieldNumber);
}
public Object fetchObjectField(int fieldNumber)
{
ExecutionContext ec = sm.getExecutionContext();
ClassLoaderResolver clr = ec.getClassLoaderResolver();
AbstractMemberMetaData mmd = sm.getClassMetaData().getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
int relationType = mmd.getRelationType(clr);
if (relationType == Relation.NONE)
{
// Object-based field (non-relation)
sm.copyFieldsFromObject(value, new int[]{fieldNumber});
return sm.provideField(fieldNumber);
}
else if (mmd.getEmbeddedMetaData() != null)
{
// TODO Implement embedded relation objects
}
else
{
switch (relationType)
{
case Relation.ONE_TO_ONE_UNI :
case Relation.ONE_TO_ONE_BI :
case Relation.MANY_TO_ONE_BI :
final AbstractClassMetaData cmd = sm.getExecutionContext().getMetaDataManager().getMetaDataForClass(mmd.getType(), clr);
final NodeList nList = ((Element) node).getElementsByTagName(mmd.getName());
if (nList.getLength() == 1)
{
Object id = ec.getApiAdapter().getNewApplicationIdentityObjectId(clr.classForName(cmd.getFullClassName()), nList.item(0).getFirstChild().getNodeValue());
Object related = ec.findObject(id, true, true, null);
if (relationType == Relation.ONE_TO_ONE_BI)
{
// Set other side of relation to avoid reloading
ObjectProvider relatedSM = ec.findObjectProvider(related);
AbstractMemberMetaData relatedMmd = mmd.getRelatedMemberMetaDataForObject(clr, sm.getObject(), related);
relatedSM.replaceField(relatedMmd.getAbsoluteFieldNumber(), sm.getObject());
}
return related;
}
return null;
case Relation.ONE_TO_MANY_UNI :
case Relation.ONE_TO_MANY_BI :
// TODO Cater for Map/array
if (mmd.hasCollection())
{
AbstractClassMetaData cmd2 = sm.getExecutionContext().getMetaDataManager().getMetaDataForClass(mmd.getCollection().getElementType(), clr);
// Get value being unmarshalled
sm.copyFieldsFromObject(value, new int[]{fieldNumber});
Collection collection = (Collection) sm.provideField(fieldNumber);
// Make sure we get the right type of element (allow for inheritance)
NodeList nLists = ((Element) node).getElementsByTagName(
XMLUtils.getElementNameForMember(mmd, FieldRole.ROLE_COLLECTION_ELEMENT));
for (int i = 0; i < nLists.getLength(); i++)
{
final String nodeValue = nLists.item(i).getFirstChild().getNodeValue();
if (nodeValue != null && nodeValue.trim().length() > 0)
{
final AbstractClassMetaData elementCmd = XMLUtils.findMetaDataForNode(doc, cmd2,
ec.getMetaDataManager(), nodeValue, clr);
if (elementCmd == null && cmd2 != null)
{
throw new NucleusException("Unable to find object of type " + cmd2.getFullClassName() + " with id=" + nodeValue);
}
Object id = ec.getApiAdapter().getNewApplicationIdentityObjectId(
clr.classForName(elementCmd.getFullClassName()), nodeValue);
Object related = ec.findObject(id, true, true, null);
if (relationType == Relation.ONE_TO_MANY_BI)
{
// Set other side of relation to avoid reloading
ObjectProvider relatedSM = ec.findObjectProvider(related);
AbstractMemberMetaData relatedMmd = relatedSM.getClassMetaData().getMetaDataForMember(mmd.getMappedBy());
relatedSM.replaceField(relatedMmd.getAbsoluteFieldNumber(), sm.getObject());
}
collection.add(related);
}
}
return sm.wrapSCOField(fieldNumber, collection, false, false, true);
}
else if (mmd.hasArray())
{
// TODO Implement support for arrays
}
else if (mmd.hasMap())
{
// TODO Implement support for maps
}
default :
break;
}
}
return null;
}
public boolean fetchBooleanField(int fieldNumber)
{
sm.copyFieldsFromObject(value, new int[]{fieldNumber});
return (Boolean) sm.provideField(fieldNumber);
}
public byte fetchByteField(int fieldNumber)
{
sm.copyFieldsFromObject(value, new int[]{fieldNumber});
return (Byte) sm.provideField(fieldNumber);
}
public char fetchCharField(int fieldNumber)
{
sm.copyFieldsFromObject(value, new int[]{fieldNumber});
return (Character) sm.provideField(fieldNumber);
}
public double fetchDoubleField(int fieldNumber)
{
sm.copyFieldsFromObject(value, new int[]{fieldNumber});
return (Double) sm.provideField(fieldNumber);
}
public float fetchFloatField(int fieldNumber)
{
sm.copyFieldsFromObject(value, new int[]{fieldNumber});
return (Float) sm.provideField(fieldNumber);
}
public int fetchIntField(int fieldNumber)
{
sm.copyFieldsFromObject(value, new int[]{fieldNumber});
return (Integer) sm.provideField(fieldNumber);
}
public long fetchLongField(int fieldNumber)
{
sm.copyFieldsFromObject(value, new int[]{fieldNumber});
return (Long) sm.provideField(fieldNumber);
}
public short fetchShortField(int fieldNumber)
{
sm.copyFieldsFromObject(value, new int[]{fieldNumber});
return (Short) sm.provideField(fieldNumber);
}
}