/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.oxm.mappings; import java.lang.reflect.Modifier; import javax.xml.namespace.QName; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; import org.eclipse.persistence.descriptors.ClassDescriptor; import org.eclipse.persistence.exceptions.DatabaseException; import org.eclipse.persistence.exceptions.DescriptorException; import org.eclipse.persistence.exceptions.XMLMarshalException; import org.eclipse.persistence.internal.descriptors.ObjectBuilder; import org.eclipse.persistence.internal.helper.DatabaseField; import org.eclipse.persistence.internal.identitymaps.CacheKey; import org.eclipse.persistence.internal.oxm.ConversionManager; import org.eclipse.persistence.internal.oxm.XMLObjectBuilder; import org.eclipse.persistence.internal.oxm.XPathEngine; import org.eclipse.persistence.internal.oxm.XPathFragment; import org.eclipse.persistence.internal.oxm.XPathQName; import org.eclipse.persistence.internal.oxm.mappings.CompositeObjectMapping; import org.eclipse.persistence.internal.queries.ContainerPolicy; import org.eclipse.persistence.internal.queries.JoinedAttributeManager; import org.eclipse.persistence.internal.sessions.AbstractRecord; import org.eclipse.persistence.internal.sessions.AbstractSession; import org.eclipse.persistence.mappings.AttributeAccessor; import org.eclipse.persistence.mappings.converters.Converter; import org.eclipse.persistence.mappings.foundation.AbstractCompositeObjectMapping; import org.eclipse.persistence.oxm.XMLConstants; import org.eclipse.persistence.oxm.XMLContext; import org.eclipse.persistence.oxm.XMLDescriptor; import org.eclipse.persistence.oxm.XMLField; import org.eclipse.persistence.oxm.XMLMarshaller; import org.eclipse.persistence.oxm.XMLUnmarshaller; import org.eclipse.persistence.oxm.mappings.converters.XMLConverter; import org.eclipse.persistence.oxm.mappings.nullpolicy.AbstractNullPolicy; import org.eclipse.persistence.oxm.mappings.nullpolicy.NullPolicy; import org.eclipse.persistence.oxm.record.DOMRecord; import org.eclipse.persistence.oxm.record.XMLRecord; import org.eclipse.persistence.platform.xml.XMLPlatformFactory; import org.eclipse.persistence.queries.ObjectBuildingQuery; import org.eclipse.persistence.sessions.Session; /** * <p>Composite object XML mappings represent a relationship between two classes. In XML, the "owned" * class may be nested with the element tag representing the "owning" class. This mapping is, by * definition, privately owned. * * <p><b>Composite object XML mappings can be used in the following scenarios</b>:<ul> * <li> Mapping into the Parent Record </li> * <li> Mapping to an Element </li> * <li> Mapping to Different Elements by Element Name </li> * <li> Mapping to Different Elements by Element Position </li> * </ul> * * <p><b>Setting the XPath</b>: TopLink XML mappings make use of XPath statements to find the relevant * data in an XML document. The XPath statement is relative to the context node specified in the descriptor. * The XPath may contain path and positional information; the last node in the XPath forms the local * root node for the composite object. The XPath is specified on the mapping using the <code>setXPath</code> * method. * * <p>The following XPath statements may be used to specify the location of XML data relating to an object's * name attribute: * * <table summary="" border="1"> * <tr> * <th id="c1" align="left">XPath</th> * <th id="c2" align="left">Description</th> * </tr> * <tr> * <td headers="c1">.</td> * <td headers="c2">Indicates "self".</td> * </tr> * <tr> * <td headers="c1">phone-number</td> * <td headers="c2">The phone-number information is stored in the phone-number element.</td> * </tr> * <tr> * <td headers="c1" style="nowrap">contact-info/phone-number</td> * <td headers="c2">The XPath statement may be used to specify any valid path.</td> * </tr> * <tr> * <td headers="c1">phone-number[2]</td> * <td headers="c2">The XPath statement may contain positional information. In this case the phone-number * information is stored in the second occurrence of the phone-number element.</td> * </tr> * </table> * * <p><b>Mapping into the Parent Record</b>: The composite object may be mapped into the parent * record in a corresponding XML document. * * <!-- * <?xml version="1.0" encoding="UTF-8"?> * <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> * <xsd:element name="customer" type="customer-type"/> * <xsd:complexType name="customer-type"> * <xsd:sequence> * <xsd:element name="first-name" type="xsd:string"/> * <xsd:element name="last-name" type="xsd:string"/> * <xsd:element name="street" type="xsd:string"/> * <xsd:element name="city" type="xsd:string"/> * </xsd:sequence> * </xsd:complexType> * </xsd:schema> * --> * * <p><em>XML Schema</em><br> * <code> * <?xml version="1.0" encoding="UTF-8"?><br> * <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"><br> *   <xsd:element name="customer" type="customer-type"/><br> *   <xsd:complexType name="customer-type"><br> *     <xsd:sequence><br> *       <xsd:element name="first-name" type="xsd:string"/><br> *       <xsd:element name="last-name" type="xsd:string"/><br> *       <xsd:element name="street" type="xsd:string"/><br> *       <xsd:element name="city" type="xsd:string"/><br> *     </xsd:sequence><br> *   </xsd:complexType><br> * </xsd:schema> * </code> * * <p><em>Code Sample</em><br> * <code> * XMLCompositeObjectMapping addressMapping = new XMLCompositeObjectMapping();<br> * addressMapping.setAttributeName("address");<br> * addressMapping.setXPath(".");<br> * addressMapping.setReferenceClass(Address.class);<br> * </code> * * <p><b>Mapping to an Element</b>: The composite object may be mapped to an element in a corresponding * XML document. * * <!-- * <?xml version="1.0" encoding="UTF-8"?> * <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> * <xsd:element name="customer" type="customer-type"/> * <xsd:complexType name="customer-type"> * <xsd:sequence> * <xsd:element name="first-name" type="xsd:string"/> * <xsd:element name="last-name" type="xsd:string"/> * <xsd:element name="address"> * <xsd:complexType> * <xsd:sequence> * <xsd:element name="street" type="xsd:string"/> * <xsd:element name="city" type="xsd:string"/> * </xsd:sequence> * </xsd:complexType> * </xsd:element> * </xsd:sequence> * </xsd:complexType> * </xsd:schema> * --> * * <p><em>XML Schema</em><br> * <code> * <?xml version="1.0" encoding="UTF-8"?><br> * <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"><br> *   <xsd:element name="customer" type="customer-type"/><br> *   <xsd:complexType name="customer-type"><br> *     <xsd:sequence><br> *       <xsd:element name="first-name" type="xsd:string"/><br> *       <xsd:element name="last-name" type="xsd:string"/><br> *       <xsd:element name="address"><br> *         <xsd:complexType><br> *           <xsd:sequence><br> *             <xsd:element name="street" type="xsd:string"/><br> *             <xsd:element name="city" type="xsd:string"/><br> *           </xsd:sequence><br> *         </xsd:complexType><br> *       </xsd:element><br> *     </xsd:sequence><br> *   </xsd:complexType><br> * </xsd:schema><br> * </code> * * <p><em>Code Sample</em><br> * <code> * XMLCompositeObjectMapping addressMapping = new XMLCompositeObjectMapping();<br> * addressMapping.setAttributeName("address");<br> * addressMapping.setXPath("address");<br> * addressMapping.setReferenceClass(Address.class);<br> * </code> * * <p><b>More Information</b>: For more information about using the XML Composite Object Mapping, see the * "Understanding XML Mappings" chapter of the Oracle TopLink Developer's Guide. * * @since Oracle TopLink 10<i>g</i> Release 2 (10.1.3) */ public class XMLCompositeObjectMapping extends AbstractCompositeObjectMapping implements XMLMapping, CompositeObjectMapping<AbstractSession, AttributeAccessor, ContainerPolicy, Converter, ClassDescriptor, DatabaseField, XMLMarshaller, Session, UnmarshalKeepAsElementPolicy, XMLUnmarshaller, XMLRecord>, XMLNillableMapping { AbstractNullPolicy nullPolicy; private XMLInverseReferenceMapping inverseReferenceMapping; private UnmarshalKeepAsElementPolicy keepAsElementPolicy; private boolean isWriteOnly; public XMLCompositeObjectMapping() { super(); // The default policy is NullPolicy nullPolicy = new NullPolicy(); } /** * Gets the AttributeAccessor that is used to get and set the value of the * container on the target object. * @deprecated Replaced by getInverseReferenceMapping().getAttributeAccessor() */ @Deprecated public AttributeAccessor getContainerAccessor() { if (this.inverseReferenceMapping == null) { return null; } return this.inverseReferenceMapping.getAttributeAccessor(); } /** * Sets the AttributeAccessor that is used to get and set the value of the * container on the target object. * * @param anAttributeAccessor - the accessor to be used. * @deprecated Replaced by getInverseReferenceMapping().setAttributeAccessor() */ @Deprecated public void setContainerAccessor(AttributeAccessor anAttributeAccessor) { if (this.inverseReferenceMapping == null) { return; } this.inverseReferenceMapping.setAttributeAccessor(anAttributeAccessor); } /** * Sets the name of the backpointer attribute on the target object. Used to * populate the backpointer. If the specified attribute doesn't exist on * the reference class of this mapping, a DescriptorException will be thrown * during initialize. * * @param attributeName - the name of the backpointer attribute to be populated * @deprecated Replaced by getInverseReferenceMapping().setAttributeName() */ @Deprecated public void setContainerAttributeName(String attributeName) { if (this.inverseReferenceMapping == null) { return; } this.inverseReferenceMapping.setAttributeName(attributeName); } /** * Gets the name of the backpointer attribute on the target object. Used to * populate the backpointer. * @deprecated Replaced by getInverseReferenceMapping().getAttributeName() */ @Deprecated public String getContainerAttributeName() { if (this.inverseReferenceMapping == null) { return null; } return this.inverseReferenceMapping.getAttributeName(); } /** * Sets the method name to be used when accessing the value of the back pointer * on the target object of this mapping. If the specified method doesn't exist * on the reference class of this mapping, a DescriptorException will be thrown * during initialize. * * @param methodName - the getter method to be used. * @deprecated Replaced by getInverseReferenceMapping().setGetMethodName() */ @Deprecated public void setContainerGetMethodName(String methodName) { if (this.inverseReferenceMapping == null) { return; } this.inverseReferenceMapping.setGetMethodName(methodName); } /** * Sets the name of the method to be used when setting the value of the back pointer * on the target object of this mapping. If the specified method doesn't exist * on the reference class of this mapping, a DescriptorException will be thrown * during initialize. * * @param methodName - the setter method to be used. * @deprecated Replaced by getInverseReferenceMapping().setSetMethodName() */ @Deprecated public void setContainerSetMethodName(String methodName) { if (this.inverseReferenceMapping == null) { return; } this.inverseReferenceMapping.setSetMethodName(methodName); } /** * Gets the name of the method to be used when accessing the value of the * back pointer on the target object of this mapping. * @deprecated Replaced by getInverseReferenceMapping().getGetMethodName() */ @Deprecated public String getContainerGetMethodName() { if (this.inverseReferenceMapping == null) { return null; } return this.inverseReferenceMapping.getGetMethodName(); } /** * Gets the name of the method to be used when setting the value of the * back pointer on the target object of this mapping. * @deprecated Replaced by getInverseReferenceMapping().getSetMethodName() */ @Deprecated public String getContainerSetMethodName() { if (this.inverseReferenceMapping == null) { return null; } return this.inverseReferenceMapping.getSetMethodName(); } public void convertClassNamesToClasses(ClassLoader classLoader){ if(XMLConstants.UNKNOWN_OR_TRANSIENT_CLASS.equals(referenceClassName)){ return; } super.convertClassNamesToClasses(classLoader); } /** * INTERNAL: * The mapping is initialized with the given session. This mapping is fully initialized * after this. */ public void initialize(AbstractSession session) throws DescriptorException { //modified so that reference class on composite mappings is no longer mandatory String referenceClassName = getReferenceClassName(); if ((this.referenceClass == null) && (referenceClassName != null)) { if (!referenceClassName.equals(XMLConstants.UNKNOWN_OR_TRANSIENT_CLASS)) { setReferenceClass(session.getDatasourcePlatform().getConversionManager().convertClassNameToClass(referenceClassName)); } } initializeReferenceDescriptorAndField(session); if(null != getContainerAccessor()) { getContainerAccessor().initializeAttributes(this.referenceClass); } } protected void initializeReferenceDescriptorAndField(AbstractSession session){ if (this.referenceClass != null) { super.initialize(session); } else { //below should be the same as AbstractCompositeObjectMapping.initialize if (this.field == null) { throw DescriptorException.fieldNameNotSetInMapping(this); } setField(getDescriptor().buildField(this.field)); setFields(collectFields()); // initialize the converter - if necessary if (hasConverter()) { getConverter().initialize(this, session); } } } /** * Set the AbstractNullPolicy on the mapping<br> * The default policy is NullPolicy.<br> * * @param aNullPolicy */ public void setNullPolicy(AbstractNullPolicy aNullPolicy) { nullPolicy = aNullPolicy; } /** * INTERNAL: * Get the AbstractNullPolicy from the Mapping.<br> * The default policy is NullPolicy.<br> * @return */ public AbstractNullPolicy getNullPolicy() { return nullPolicy; } /** * INTERNAL: */ public boolean isXMLMapping() { return true; } /** * Get the XPath String * @return String the XPath String associated with this Mapping */ public String getXPath() { return getField().getName(); } /** * Set the Mapping field name attribute to the given XPath String * @param xpathString String */ public void setXPath(String xpathString) { this.setField(new XMLField(xpathString)); } @Override protected Object buildCompositeRow(Object attributeValue, AbstractSession session, AbstractRecord databaseRow, WriteType writeType) { XMLDescriptor xmlReferenceDescriptor = null; try{ xmlReferenceDescriptor = (XMLDescriptor) getReferenceDescriptor(attributeValue, session); }catch(Exception e){ //do nothing } XMLField xmlFld = (XMLField) getField(); if (xmlFld.hasLastXPathFragment() && xmlFld.getLastXPathFragment().hasLeafElementType()) { XMLRecord xmlRec = (XMLRecord) databaseRow; xmlRec.setLeafElementType(xmlFld.getLastXPathFragment().getLeafElementType()); } XMLRecord parent = (XMLRecord) databaseRow; if (xmlReferenceDescriptor != null) { return buildCompositeRowForDescriptor(xmlReferenceDescriptor, attributeValue, session, parent, writeType); } else { if (attributeValue instanceof Element && getKeepAsElementPolicy() == UnmarshalKeepAsElementPolicy.KEEP_UNKNOWN_AS_ELEMENT) { return new DOMRecord((Element) attributeValue); }else{ Node newNode = XPathEngine.getInstance().create((XMLField)getField(), parent.getDOM(), attributeValue, session); DOMRecord newRow = new DOMRecord(newNode); return newRow; } } } protected AbstractRecord buildCompositeRowForDescriptor(ClassDescriptor classDesc, Object attributeValue, AbstractSession session, XMLRecord parentRow, WriteType writeType) { XMLObjectBuilder objectBuilder = (XMLObjectBuilder) classDesc.getObjectBuilder(); XMLRecord child = (XMLRecord) objectBuilder.createRecordFor(attributeValue, (XMLField) getField(), parentRow, this); child.setNamespaceResolver(parentRow.getNamespaceResolver()); child.setSession(session); objectBuilder.buildIntoNestedRow(child, attributeValue, session, (XMLDescriptor)getReferenceDescriptor(), (XMLField) getField()); return child; } @Override protected Object buildCompositeObject(ObjectBuilder objectBuilder, AbstractRecord nestedRow, ObjectBuildingQuery query, CacheKey parentCacheKey, JoinedAttributeManager joinManager, AbstractSession targetSession) { return objectBuilder.buildObject(query, nestedRow, joinManager); } public Object readFromRowIntoObject(AbstractRecord databaseRow, JoinedAttributeManager joinManager, Object targetObject, CacheKey parentCacheKey, ObjectBuildingQuery sourceQuery, AbstractSession executionSession, boolean isTargetProtected) throws DatabaseException { Object fieldValue = databaseRow.getIndicatingNoEntry(getField()); // 20071002: noEntry ineffective as a check for an absent node, empty nodes are DOMRecords, absent nodes are null) // if(fieldValue == AbstractRecord.noEntry && !getNullPolicy().getIsSetPerformedForAbsentNode()) { // Do not perform a set for an absent node // return null; // } else { // Check for absent nodes based on policy flag if ((null == fieldValue) || fieldValue instanceof String) { if (getNullPolicy().getIsSetPerformedForAbsentNode()) { setAttributeValueInObject(targetObject, null); } else { return null; } return null; } // } // Empty or xsi:nil nodes (non-absent) will arrive here along with populated nodes XMLRecord nestedRow = (XMLRecord) this.getDescriptor().buildNestedRowFromFieldValue(fieldValue); // Check the policy to see if this DOM empty/xsi:nil or filled record represents null if (getNullPolicy().valueIsNull((Element) nestedRow.getDOM())) { setAttributeValueInObject(targetObject, null); return null; } Object attributeValue = valueFromRow(fieldValue, nestedRow, joinManager, sourceQuery, executionSession, isTargetProtected); setAttributeValueInObject(targetObject, attributeValue); if(null != getContainerAccessor()) { if(this.inverseReferenceMapping.getContainerPolicy() == null) { getContainerAccessor().setAttributeValueInObject(attributeValue, targetObject); } else { Object backpointerContainer = getContainerAccessor().getAttributeValueFromObject(attributeValue); if(backpointerContainer == null) { backpointerContainer = this.inverseReferenceMapping.getContainerPolicy().containerInstance(); getContainerAccessor().setAttributeValueInObject(attributeValue, backpointerContainer); } this.inverseReferenceMapping.getContainerPolicy().addInto(targetObject, backpointerContainer, executionSession); } } return attributeValue; } public Object valueFromRow(Object fieldValue, XMLRecord nestedRow, JoinedAttributeManager joinManager, ObjectBuildingQuery sourceQuery, AbstractSession executionSession, boolean isTargetProtected) throws DatabaseException { // pretty sure we can ignore inheritance here: Object toReturn = null; // Use local descriptor - not the instance variable on DatabaseMapping ClassDescriptor aDescriptor = getReferenceDescriptor((DOMRecord) nestedRow); if (aDescriptor == null) { if ((getKeepAsElementPolicy() == UnmarshalKeepAsElementPolicy.KEEP_UNKNOWN_AS_ELEMENT) || (getKeepAsElementPolicy() == UnmarshalKeepAsElementPolicy.KEEP_ALL_AS_ELEMENT)) { XMLPlatformFactory.getInstance().getXMLPlatform().namespaceQualifyFragment((Element) nestedRow.getDOM()); toReturn = nestedRow.getDOM(); toReturn = convertDataValueToObjectValue(toReturn, executionSession, nestedRow.getUnmarshaller()); //try simple case toReturn = convertToSimpleTypeIfPresent(toReturn, nestedRow, executionSession); return toReturn; } else { NodeList children =((Element) nestedRow.getDOM()).getChildNodes(); for(int i=0, childrenLength=children.getLength(); i<childrenLength ; i++){ Node nextNode = children.item(i); if(nextNode.getNodeType() == nextNode.ELEMENT_NODE){ //complex child String type = ((Element) nestedRow.getDOM()).getAttributeNS(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, XMLConstants.SCHEMA_TYPE_ATTRIBUTE); if(type != null && type.length() > 0) { throw XMLMarshalException.unknownXsiTypeValue(type, (CompositeObjectMapping) this); } else { throw XMLMarshalException.noDescriptorFound((CompositeObjectMapping) this); } } } //simple case toReturn = convertToSimpleTypeIfPresent(toReturn, nestedRow, executionSession); } } else { if (aDescriptor.hasInheritance()) { Class classValue = aDescriptor.getInheritancePolicy().classFromRow(nestedRow, executionSession); if (classValue == null) { // no xsi:type attribute - look for type indicator on the field QName leafElementType = ((XMLField) getField()).getLeafElementType(); if (leafElementType != null) { XPathQName leafElementXPathQName = new XPathQName(leafElementType, ((XMLRecord) nestedRow).isNamespaceAware()); Object indicator = aDescriptor.getInheritancePolicy().getClassIndicatorMapping().get(leafElementXPathQName); if(indicator != null) { classValue = (Class) indicator; } } } if (classValue != null) { aDescriptor = this.getReferenceDescriptor(classValue, executionSession); } else { // since there is no xsi:type attribute or leaf element type set, // use the reference descriptor - make sure it is non-abstract if (Modifier.isAbstract(aDescriptor.getJavaClass().getModifiers())) { // throw an exception throw DescriptorException.missingClassIndicatorField((org.eclipse.persistence.internal.oxm.record.XMLRecord) nestedRow, aDescriptor.getInheritancePolicy().getDescriptor()); } } } ObjectBuilder objectBuilder = aDescriptor.getObjectBuilder(); toReturn = buildCompositeObject(objectBuilder, nestedRow, sourceQuery, null, joinManager, executionSession); } if (getConverter() != null) { if (getConverter() instanceof XMLConverter) { toReturn = ((XMLConverter) getConverter()).convertDataValueToObjectValue(toReturn, executionSession, nestedRow.getUnmarshaller()); } else { toReturn = getConverter().convertDataValueToObjectValue(toReturn, executionSession); } } return toReturn; } private Object convertToSimpleTypeIfPresent(Object toReturn, XMLRecord nestedRow, AbstractSession executionSession){ Node textchild = ((Element) nestedRow.getDOM()).getFirstChild(); String stringValue = null; if ((textchild != null) && (textchild.getNodeType() == Node.TEXT_NODE)) { stringValue = ((Text) textchild).getNodeValue(); if(stringValue != null && getKeepAsElementPolicy() != UnmarshalKeepAsElementPolicy.KEEP_UNKNOWN_AS_ELEMENT && getKeepAsElementPolicy()!=UnmarshalKeepAsElementPolicy.KEEP_ALL_AS_ELEMENT){ toReturn = stringValue; } } if ((stringValue == null) || stringValue.length() == 0) { return toReturn; } String type = ((Element) nestedRow.getDOM()).getAttributeNS(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, XMLConstants.SCHEMA_TYPE_ATTRIBUTE); if ((null != type) && type.length() > 0) { XPathFragment typeFragment = new XPathFragment(type); String namespaceURI = nestedRow.resolveNamespacePrefix(typeFragment.getPrefix()); QName schemaTypeQName = new QName(namespaceURI, typeFragment.getLocalName()); ConversionManager conversionManager = (ConversionManager) executionSession.getDatasourcePlatform().getConversionManager(); Class theClass = conversionManager.javaType(schemaTypeQName); if (theClass != null) { toReturn = conversionManager.convertObject(stringValue, theClass, schemaTypeQName); } } return toReturn; } public Object valueFromRow(AbstractRecord row, JoinedAttributeManager joinManager, ObjectBuildingQuery sourceQuery, CacheKey cacheKey, AbstractSession executionSession, boolean isTargetProtected, Boolean[] wasCacheUsed) throws DatabaseException { Object fieldValue = row.get(this.getField()); // BUG#2667762 there could be whitespace in the row instead of null if ((fieldValue == null) || (fieldValue instanceof String)) { return null; } XMLRecord nestedRow = (XMLRecord) this.getDescriptor().buildNestedRowFromFieldValue(fieldValue); // Check the policy to see if this DOM record represents null if (getNullPolicy().valueIsNull((Element) nestedRow.getDOM())) { return null; } return valueFromRow(fieldValue, nestedRow, joinManager, sourceQuery, executionSession, isTargetProtected); } /** * INTERNAL: */ @Override public void writeFromObjectIntoRow(Object object, AbstractRecord databaseRow, AbstractSession session, WriteType writeType) throws DescriptorException { if (this.isReadOnly()) { return; } Object attributeValue = this.getAttributeValueFromObject(object); writeSingleValue(attributeValue, object, (XMLRecord) databaseRow, session); } public void writeSingleValue(Object value, Object parent, XMLRecord record, AbstractSession session) { Object attributeValue = convertObjectValueToDataValue(value, session, record.getMarshaller()); // handle "self" xpath if (((XMLField) getField()).isSelfField()) { if (((keepAsElementPolicy == UnmarshalKeepAsElementPolicy.KEEP_UNKNOWN_AS_ELEMENT) || (keepAsElementPolicy == UnmarshalKeepAsElementPolicy.KEEP_ALL_AS_ELEMENT)) && attributeValue instanceof org.w3c.dom.Node) { //write out node org.w3c.dom.Document doc = record.getDocument(); Node root = record.getDOM(); NodeList children = ((Node) attributeValue).getChildNodes(); for(int i=0,childrenLength=children.getLength();i<childrenLength; i++){ Node importedCopy = doc.importNode(children.item(i), true); root.appendChild(importedCopy); } }else{ ClassDescriptor desc; if(null == attributeValue) { desc = this.getReferenceDescriptor(); } else { desc = this.getReferenceDescriptor(attributeValue.getClass(), session); } if(desc != null){ XMLObjectBuilder objectBuilder = (XMLObjectBuilder)desc.getObjectBuilder(); objectBuilder.buildIntoNestedRow(record, attributeValue, session, (XMLDescriptor)getReferenceDescriptor(), (XMLField) getField()); }else{ //simple case record.put(this.getField(), attributeValue); } } } else { Object fieldValue = null; if (attributeValue != null) { fieldValue = buildCompositeRow(attributeValue, session, record, WriteType.UNDEFINED); } else if (getNullPolicy().compositeObjectMarshal(record, parent, (XMLField) getField(), session)) { // If the null policy marshal method returns true (i.e. marshalled something) // don't add/put null in the record return; } // handle document preservation record.put(this.getField(), fieldValue); } } public void configureNestedRow(AbstractRecord parent, AbstractRecord child) { XMLRecord parentRecord = (XMLRecord) parent; XMLRecord childRecord = (XMLRecord) child; childRecord.setUnmarshaller(parentRecord.getUnmarshaller()); childRecord.setOwningObject(parentRecord.getCurrentObject()); } public ClassDescriptor getReferenceDescriptor(DOMRecord xmlRecord) { ClassDescriptor returnDescriptor = referenceDescriptor; if (returnDescriptor == null) { // Try to find a descriptor based on the schema type String type = ((Element) xmlRecord.getDOM()).getAttributeNS(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, XMLConstants.SCHEMA_TYPE_ATTRIBUTE); if ((null != type) && type.length() > 0) { XPathFragment typeFragment = new XPathFragment(type); String namespaceURI = xmlRecord.resolveNamespacePrefix(typeFragment.getPrefix()); typeFragment.setNamespaceURI(namespaceURI); returnDescriptor = xmlRecord.getUnmarshaller().getXMLContext().getDescriptorByGlobalType(typeFragment); } else { //try leaf element type QName leafType = ((XMLField) getField()).getLastXPathFragment().getLeafElementType(); if (leafType != null) { XPathFragment frag = new XPathFragment(); String xpath = leafType.getLocalPart(); String uri = leafType.getNamespaceURI(); if ((uri != null) && uri.length() > 0) { frag.setNamespaceURI(uri); String prefix = ((XMLDescriptor) getDescriptor()).getNonNullNamespaceResolver().resolveNamespaceURI(uri); if ((prefix != null) && prefix.length() > 0) { xpath = prefix + XMLConstants.COLON + xpath; } } frag.setXPath(xpath); returnDescriptor = xmlRecord.getUnmarshaller().getXMLContext().getDescriptorByGlobalType(frag); } } } return returnDescriptor; } protected ClassDescriptor getReferenceDescriptor(Class theClass, AbstractSession session) { if ((getReferenceDescriptor() != null) && getReferenceDescriptor().getJavaClass().equals(theClass)) { return getReferenceDescriptor(); } ClassDescriptor subDescriptor = session.getDescriptor(theClass); if (subDescriptor == null && getKeepAsElementPolicy() != UnmarshalKeepAsElementPolicy.KEEP_UNKNOWN_AS_ELEMENT) { throw DescriptorException.noSubClassMatch(theClass, this); } else { return subDescriptor; } } public UnmarshalKeepAsElementPolicy getKeepAsElementPolicy() { return keepAsElementPolicy; } public void setKeepAsElementPolicy(UnmarshalKeepAsElementPolicy keepAsElementPolicy) { this.keepAsElementPolicy = keepAsElementPolicy; } protected XMLDescriptor getDescriptor(XMLRecord xmlRecord, AbstractSession session, QName rootQName) throws XMLMarshalException { if (rootQName == null) { rootQName = new QName(xmlRecord.getNamespaceURI(), xmlRecord.getLocalName()); } XMLContext xmlContext = xmlRecord.getUnmarshaller().getXMLContext(); XMLDescriptor xmlDescriptor = xmlContext.getDescriptor(rootQName); if (null == xmlDescriptor) { if (!((getKeepAsElementPolicy() == UnmarshalKeepAsElementPolicy.KEEP_UNKNOWN_AS_ELEMENT) || (getKeepAsElementPolicy() == UnmarshalKeepAsElementPolicy.KEEP_ALL_AS_ELEMENT))) { throw XMLMarshalException.noDescriptorWithMatchingRootElement(xmlRecord.getLocalName()); } } return xmlDescriptor; } public void setIsWriteOnly(boolean b) { isWriteOnly = b; } public boolean isWriteOnly() { return isWriteOnly; } public void preInitialize(AbstractSession session) throws DescriptorException { getAttributeAccessor().setIsWriteOnly(this.isWriteOnly()); getAttributeAccessor().setIsReadOnly(this.isReadOnly()); super.preInitialize(session); } public void setAttributeValueInObject(Object object, Object value) throws DescriptorException { if(isWriteOnly()) { return; } super.setAttributeValueInObject(object, value); } public XMLInverseReferenceMapping getInverseReferenceMapping() { return inverseReferenceMapping; } void setInverseReferenceMapping(XMLInverseReferenceMapping inverseReferenceMapping) { this.inverseReferenceMapping = inverseReferenceMapping; } /** * INTERNAL * @since EclipseLink 2.5.0 */ public Object convertObjectValueToDataValue(Object value, Session session, XMLMarshaller marshaller) { if (hasConverter()) { if (converter instanceof XMLConverter) { return ((XMLConverter)converter).convertObjectValueToDataValue(value, session, marshaller); } else { return converter.convertObjectValueToDataValue(value, session); } } return value; } /** * INTERNAL * @since EclipseLink 2.5.0 */ public Object convertDataValueToObjectValue(Object fieldValue, Session session, XMLUnmarshaller unmarshaller) { if (hasConverter()) { if (converter instanceof XMLConverter) { return ((XMLConverter)converter).convertDataValueToObjectValue(fieldValue, session, unmarshaller); } else { return converter.convertDataValueToObjectValue(fieldValue, session); } } return fieldValue; } }