All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.persistence.oxm.XMLDescriptor Maven / Gradle / Ivy

There is a newer version: 5.0.0-B03
Show newest version
/*
 * Copyright (c) 1998, 2024 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 v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
//     14/05/2012-2.4 Guy Pelletier
//       - 376603: Provide for table per tenant support for multitenant applications
package org.eclipse.persistence.oxm;

import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.InheritancePolicy;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.exceptions.XMLMarshalException;
import org.eclipse.persistence.internal.descriptors.InstantiationPolicy;
import org.eclipse.persistence.internal.descriptors.ObjectBuilder;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.helper.DatabaseTable;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.oxm.Root;
import org.eclipse.persistence.internal.oxm.TreeObjectBuilder;
import org.eclipse.persistence.internal.oxm.Unmarshaller;
import org.eclipse.persistence.internal.oxm.XPathFragment;
import org.eclipse.persistence.internal.oxm.XPathQName;
import org.eclipse.persistence.internal.oxm.mappings.Descriptor;
import org.eclipse.persistence.internal.oxm.record.UnmarshalRecord;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.mappings.AggregateMapping;
import org.eclipse.persistence.mappings.AttributeAccessor;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.foundation.AbstractDirectMapping;
import org.eclipse.persistence.oxm.mappings.XMLChoiceCollectionMapping;
import org.eclipse.persistence.oxm.mappings.XMLChoiceObjectMapping;
import org.eclipse.persistence.oxm.mappings.XMLCompositeCollectionMapping;
import org.eclipse.persistence.oxm.mappings.XMLCompositeDirectCollectionMapping;
import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping;
import org.eclipse.persistence.oxm.mappings.XMLDirectMapping;
import org.eclipse.persistence.oxm.mappings.XMLMapping;
import org.eclipse.persistence.oxm.record.XMLRecord;
import org.eclipse.persistence.oxm.schema.XMLSchemaReference;
import org.eclipse.persistence.queries.AttributeGroup;
import org.eclipse.persistence.queries.DoesExistQuery;

import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.StringTokenizer;
import java.util.Vector;

/**
 * Use an XML project for nontransactional, nonpersistent (in-memory) conversions between Java objects and XML documents.
 * 

* An XMLDescriptor is a set of mappings that describe how an objects's data is to be represented in an * XML document. XML descriptors describe Java objects that you map to simple and complex types defined * by an XML schema document (XSD). Using XML descriptors in an EclipseLink XML project, you can configure XML mappings. * * @see org.eclipse.persistence.oxm.mappings */ public class XMLDescriptor extends ClassDescriptor implements Descriptor{ /* * Character used to separate individual xPath elements. * TODO: Use some global value reference. */ private static final char XPATH_FRAGMENT_SEPARATOR = '/'; private NamespaceResolver namespaceResolver; private XMLSchemaReference schemaReference; private boolean shouldPreserveDocument = false; private XMLField defaultRootElementField; private boolean sequencedObject = false; private boolean isWrapper = false; private boolean resultAlwaysXMLRoot = false; private boolean lazilyInitialized = false; private AttributeAccessor locationAccessor = null; private boolean hasReferenceMappings = false; /** * PUBLIC: * Return a new XMLDescriptor. */ public XMLDescriptor() { this.tables = new ArrayList<>(3); this.mappings = new ArrayList<>(); this.primaryKeyFields = null; this.fields = new ArrayList<>(); this.allFields = new ArrayList<>(); this.constraintDependencies = Collections.emptyList(); this.multipleTableForeignKeys = Collections.emptyMap(); this.queryKeys = Collections.emptyMap(); this.initializationStage = UNINITIALIZED; this.interfaceInitializationStage = UNINITIALIZED; this.descriptorType = NORMAL; this.shouldOrderMappings = true; this.shouldBeReadOnly = false; this.shouldAlwaysConformResultsInUnitOfWork = false; this.shouldAcquireCascadedLocks = false; this.hasSimplePrimaryKey = false; this.idValidation = null; this.derivesIdMappings = Collections.emptyMap(); this.additionalWritableMapKeyFields = Collections.emptyList(); // Policies this.objectBuilder = new TreeObjectBuilder(this); this.shouldOrderMappings = false; descriptorIsAggregate(); } /** * PUBLIC: * Return the default root element name for the ClassDescriptor * This value is stored in place of a table name * This value is mandatory for all root objects * @return the default root element specified on this ClassDescriptor */ @Override public String getDefaultRootElement() { if (getTables().isEmpty()) { return null; } return getTables().get(0).getName(); } /** * PUBLIC: * Return if unmapped information from the XML document should be maintained for this * descriptor * By default unmapped data is not preserved. * @return if this descriptor should preserve unmapped data */ @Override public boolean shouldPreserveDocument() { return this.shouldPreserveDocument; } /** * PUBLIC: * Specifies that object built from this descriptor should retain any unmapped * information from their original XML Document when being written back out. * By default unmapped data is not preserved. * * @param shouldPreserveDocument if this descriptor should preserve unmapped data */ public void setShouldPreserveDocument(boolean shouldPreserveDocument) { this.shouldPreserveDocument = shouldPreserveDocument; } /** * PUBLIC: * Add a root element name for the Descriptor * This value is stored in place of a table name * @param rootElementName a root element to specify on this Descriptor */ @Override public void addRootElement(String rootElementName) { if (rootElementName != null) { if (!getTableNames().contains(rootElementName)) { addTableName(rootElementName); } } } /** * PUBLIC: * Return the default root element name for the ClassDescriptor * This value is stored in place of a table name * This value is mandatory for all root objects * @param newDefaultRootElement the default root element to specify on this ClassDescriptor */ @Override public void setDefaultRootElement(String newDefaultRootElement) { if(setDefaultRootElementField(newDefaultRootElement)) { int index = getTableNames().indexOf(newDefaultRootElement); if (index == 0) { return; } DatabaseTable databaseTable = new DatabaseTable(); databaseTable.setUseDelimiters(false); databaseTable.setName(newDefaultRootElement); if (index >= 0) { getTables().remove(index); getTables().add(0, databaseTable); } else { getTables().add(0, databaseTable); } } } /** * PUBLIC: * Return the NamespaceResolver associated with this descriptor * @return the NamespaceResolver associated with this descriptor * @see org.eclipse.persistence.oxm.NamespaceResolver */ @Override public NamespaceResolver getNamespaceResolver() { return namespaceResolver; } @Override public NamespaceResolver getNonNullNamespaceResolver() { if (namespaceResolver == null) { namespaceResolver = new NamespaceResolver(); } return namespaceResolver; } /** * PUBLIC: * The inheritance policy is used to define how a descriptor takes part in inheritance. * All inheritance properties for both child and parent classes is configured in inheritance policy. * Caution must be used in using this method as it lazy initializes an inheritance policy. * Calling this on a descriptor that does not use inheritance will cause problems, #hasInheritance() must always first be called. * @return the InheritancePolicy associated with this descriptor */ @Override public InheritancePolicy getInheritancePolicy() { if (inheritancePolicy == null) { // Lazy initialize to conserve space in non-inherited classes. setInheritancePolicy(new org.eclipse.persistence.internal.oxm.QNameInheritancePolicy(this)); } return inheritancePolicy; } /** * PUBLIC: * Set the NamespaceResolver to associate with this descriptor * @param newNamespaceResolver the NamespaceResolver to associate with this descriptor * @see org.eclipse.persistence.oxm.NamespaceResolver */ @Override public void setNamespaceResolver(NamespaceResolver newNamespaceResolver) { namespaceResolver = newNamespaceResolver; } /** * PUBLIC: * Return the SchemaReference associated with this descriptor * @return the SchemaReference associated with this descriptor * @see org.eclipse.persistence.oxm.schema */ @Override public XMLSchemaReference getSchemaReference() { return schemaReference; } /** * PUBLIC: * Set the SchemaReference to associate with this descriptor * @param newSchemaReference the SchemaReference to associate with this descriptor * @see org.eclipse.persistence.oxm.schema */ @Override public void setSchemaReference(XMLSchemaReference newSchemaReference) { schemaReference = newSchemaReference; } /** * PUBLIC: * Return if the descriptor maps to XML. */ @Override public boolean isXMLDescriptor() { return true; } /** * If true, the descriptor may be lazily initialized. This is useful if the * descriptor may not get used. */ @Override public boolean isLazilyInitialized() { return lazilyInitialized; } /** * Specify in the descriptor may be lazily initialized. The default is * false. */ public void setLazilyInitialized(boolean shouldLazyInitiailize) { this.lazilyInitialized = shouldLazyInitiailize; } @Override public List getPrimaryKeyFieldNames() { if(null == primaryKeyFields) { return new ArrayList<>(0); } return super.getPrimaryKeyFieldNames(); } @Override protected void validateMappingType(DatabaseMapping mapping) { if (!(mapping.isXMLMapping())) { throw DescriptorException.invalidMappingType(mapping); } } /* * INTERNAL: * Avoid SDK initialization. public void setQueryManager(DescriptorQueryManager queryManager) { this.queryManager = queryManager; if (queryManager != null) { queryManager.setDescriptor(this); } }*/ /** * INTERNAL: * Build(if necessary) and return the nested XMLRecord from the specified field value. * The field value should be an XMLRecord or and XMLElement */ @Override public AbstractRecord buildNestedRowFromFieldValue(Object fieldValue) { if (fieldValue instanceof XMLRecord) { return (XMLRecord) fieldValue; } // BUG#2667762 - If the tag was empty this could be a string of whitespace. if (!(fieldValue instanceof Vector nestedRows)) { return getObjectBuilder().createRecord(null); } if (nestedRows.isEmpty()) { return getObjectBuilder().createRecord(null); } else { // BUG#2667762 - If the tag was empty this could be a string of whitespace. if (!(nestedRows.get(0) instanceof AbstractRecord)) { return getObjectBuilder().createRecord(null); } return (XMLRecord) nestedRows.get(0); } } /** * INTERNAL: * Build(if necessary) and return a Vector of the nested XMLRecords from the specified field value. * The field value should be a Vector, an XMLRecord, or an XMLElement */ @Override public List buildNestedRowsFromFieldValue(Object fieldValue, AbstractSession session) { // BUG#2667762 - If the tag was empty this could be a string of whitespace. if (!(fieldValue instanceof Vector)) { return new Vector<>(0); } return (Vector) fieldValue; } /** * Return a new direct/basic mapping for this type of descriptor. */ @Override public AbstractDirectMapping newDirectMapping() { return new XMLDirectMapping(); } /** * Return a new aggregate/embedded mapping for this type of descriptor. */ @Override public AggregateMapping newAggregateMapping() { return new XMLCompositeObjectMapping(); } /** * Return a new aggregate collection/element collection mapping for this type of descriptor. */ @Override public DatabaseMapping newAggregateCollectionMapping() { return new XMLCompositeCollectionMapping(); } /** * Return a new direct collection/element collection mapping for this type of descriptor. */ @Override public DatabaseMapping newDirectCollectionMapping() { return new XMLCompositeDirectCollectionMapping(); } /** * PUBLIC: * Add a direct mapping to the receiver. The new mapping specifies that * an instance variable of the class of objects which the receiver describes maps in * the default manner for its type to the indicated database field. * * @param attributeName the name of an instance variable of the * class which the receiver describes. * @param xpathString the xpath of the xml element or attribute which corresponds * with the designated instance variable. * @return The newly created DatabaseMapping is returned. */ @Override public DatabaseMapping addDirectMapping(String attributeName, String xpathString) { XMLDirectMapping mapping = new XMLDirectMapping(); mapping.setAttributeName(attributeName); mapping.setXPath(xpathString); return addMapping(mapping); } /** * PUBLIC: * Add a direct to node mapping to the receiver. The new mapping specifies that * a variable accessed by the get and set methods of the class of objects which * the receiver describes maps in the default manner for its type to the indicated * database field. */ @Override public DatabaseMapping addDirectMapping(String attributeName, String getMethodName, String setMethodName, String xpathString) { XMLDirectMapping mapping = new XMLDirectMapping(); mapping.setAttributeName(attributeName); mapping.setSetMethodName(setMethodName); mapping.setGetMethodName(getMethodName); mapping.setXPath(xpathString); return addMapping(mapping); } @Override public void addPrimaryKeyFieldName(String fieldName) { addPrimaryKeyField(new XMLField(fieldName)); } @Override public void addPrimaryKeyField(DatabaseField field) { if (!(field instanceof XMLField)) { String fieldName = field.getName(); field = new XMLField(fieldName); } if(null == primaryKeyFields) { primaryKeyFields = new ArrayList<>(1); } super.addPrimaryKeyField(field); } @Override public void setPrimaryKeyFields(List thePrimaryKeyFields) { if(null == thePrimaryKeyFields) { return; } List xmlFields = new ArrayList(thePrimaryKeyFields.size()); Iterator it = thePrimaryKeyFields.iterator(); while (it.hasNext()) { DatabaseField field = it.next(); if (!(field instanceof XMLField)) { String fieldName = field.getName(); field = new XMLField(fieldName); } xmlFields.add(field); } super.setPrimaryKeyFields(xmlFields); } /** * INTERNAL: * Extract the direct values from the specified field value. * Return them in a vector. * The field value could be a vector or could be a text value if only a single value. */ @Override public List buildDirectValuesFromFieldValue(Object fieldValue) throws DatabaseException { if (!(fieldValue instanceof Vector)) { List fieldValues = new Vector(1); fieldValues.add(fieldValue); return fieldValues; } return (List) fieldValue; } /** * INTERNAL: * Build the appropriate field value for the specified * set of direct values. * The database better be expecting a Vector. */ @Override public Object buildFieldValueFromDirectValues(Vector directValues, String elementDataTypeName, AbstractSession session) throws DatabaseException { return directValues; } /** * INTERNAL: * Build and return the appropriate field value for the specified * set of nested rows. */ @Override public Object buildFieldValueFromNestedRows(Vector nestedRows, String structureName, AbstractSession session) throws DatabaseException { return nestedRows; } /** * INTERNAL: * A DatabaseField is built from the given field name. */ @Override public DatabaseField buildField(String fieldName) { XMLField xmlField = new XMLField(fieldName); xmlField.setNamespaceResolver(this.getNamespaceResolver()); //xmlField.initialize(); return xmlField; } /** * INTERNAL: * This is used only in initialization. */ @Override public DatabaseField buildField(DatabaseField field) { try { XMLField xmlField = (XMLField) field; xmlField.setNamespaceResolver(this.getNamespaceResolver()); xmlField.initialize(); } catch (ClassCastException e) { // Assumes field is always an XMLField } return super.buildField(field); } /** * INTERNAL: * This is needed by regular aggregate descriptors (because they require review); * but not by XML aggregate descriptors. */ @Override public void initializeAggregateInheritancePolicy(AbstractSession session) { // do nothing, since the parent descriptor was already modified during pre-initialize } @Override public void setTableNames(List tableNames) { if (null != tableNames && !tableNames.isEmpty()) { setDefaultRootElementField(tableNames.get(0)); } super.setTableNames(tableNames); } /** * INTERNAL: * Sets the tables */ @Override public void setTables(List theTables) { super.setTables(theTables); } /** * INTERNAL: * Allow the descriptor to initialize any dependencies on this session. */ @Override public void preInitialize(AbstractSession session) throws DescriptorException { // Avoid repetitive initialization (this does not solve loops) if (isInitialized(PREINITIALIZED)) { return; } setInitializationStage(PREINITIALIZED); // Allow mapping pre init, must be done before validate. for (DatabaseMapping mapping: getMappings()) { try { mapping.preInitialize(session); } catch (DescriptorException exception) { session.getIntegrityChecker().handleError(exception); } } getCachePolicy().useNoIdentityMap(); getQueryManager().getDoesExistQuery().setExistencePolicy(DoesExistQuery.CheckDatabase); validateBeforeInitialization(session); preInitializeInheritancePolicy(session); verifyTableQualifiers(session.getDatasourcePlatform()); initializeProperties(session); if (hasInterfacePolicy()) { preInterfaceInitialization(session); } getCachePolicy().assignDefaultValues(session); } @Override protected void preInitializeInheritancePolicy(AbstractSession session) throws DescriptorException { super.preInitializeInheritancePolicy(session); // Make sure that parent is already preinitialized if (hasInheritance()) { if(isChildDescriptor()) { XMLDescriptor parentDescriptor = (XMLDescriptor) getInheritancePolicy().getParentDescriptor(); NamespaceResolver parentNamespaceResolver = parentDescriptor.getNamespaceResolver(); if(null != parentNamespaceResolver && parentNamespaceResolver != namespaceResolver) { if(null == namespaceResolver) { namespaceResolver = getNonNullNamespaceResolver(); } if(parentNamespaceResolver.hasPrefixesToNamespaces()) { for(Entry entry : parentNamespaceResolver.getPrefixesToNamespaces().entrySet()) { String namespaceURI = namespaceResolver.resolveNamespacePrefix(entry.getKey()); if(null == namespaceURI) { namespaceResolver.put(entry.getKey(), entry.getValue()); } else if(!namespaceURI.equals(entry.getValue())) { throw XMLMarshalException.subclassAttemptedToOverrideNamespaceDeclaration(entry.getKey(), getJavaClassName(), namespaceURI, parentDescriptor.getJavaClassName(), entry.getValue()); } } } } } // The default table will be set in this call once the duplicate // tables have been removed. getInheritancePolicy().preInitialize(session); } else { // This must be done now, after validate, before init anything else. setInternalDefaultTable(); } } /** * INTERNAL: * Post initializations after mappings are initialized. */ @Override public void postInitialize(AbstractSession session) throws DescriptorException { // Avoid repetitive initialization (this does not solve loops) if (isInitialized(POST_INITIALIZED) || isInvalid()) { return; } setInitializationStage(POST_INITIALIZED); // Make sure that child is post initialized, // this initialize bottom up, unlike the two other phases that to top down. if (hasInheritance()) { for (ClassDescriptor child : getInheritancePolicy().getChildDescriptors()) { child.postInitialize(session); } } // Allow mapping to perform post initialization. for (DatabaseMapping mapping : getMappings()) { // This causes post init to be called multiple times in inheritance. mapping.postInitialize(session); } if (hasInheritance()) { getInheritancePolicy().postInitialize(session); } //PERF: Ensure that the identical primary key fields are used to avoid equals. if(null != primaryKeyFields) { for (int index = (primaryKeyFields.size() - 1); index >= 0; index--) { DatabaseField primaryKeyField = getPrimaryKeyFields().get(index); int fieldIndex = getFields().indexOf(primaryKeyField); // Aggregate/agg-collections may not have a mapping for pk field. if (fieldIndex != -1) { primaryKeyField = getFields().get(fieldIndex); getPrimaryKeyFields().set(index, primaryKeyField); } } } // Index and classify fields and primary key. // This is in post because it needs field classification defined in initializeMapping // this can come through a 1:1 so requires all descriptors to be initialized (mappings). // May 02, 2000 - Jon D. for (int index = 0; index < getFields().size(); index++) { DatabaseField field = getFields().get(index); if (field.getType() == null) { DatabaseMapping mapping = getObjectBuilder().getMappingForField(field); if (mapping != null) { field.setType(mapping.getFieldClassification(field)); } } field.setIndex(index); } validateAfterInitialization(session); } /** * INTERNAL: * Initialize the mappings as a separate step. * This is done as a separate step to ensure that inheritance has been first resolved. */ @Override public void initialize(AbstractSession session) throws DescriptorException { if (this.hasInheritance()) { ((org.eclipse.persistence.internal.oxm.QNameInheritancePolicy) this.getInheritancePolicy()).setNamespaceResolver(this.getNamespaceResolver()); } if(null != this.defaultRootElementField) { defaultRootElementField.setNamespaceResolver(this.namespaceResolver); defaultRootElementField.initialize(); } if(schemaReference != null && schemaReference.getSchemaContext() != null && (schemaReference.getType() == XMLSchemaReference.COMPLEX_TYPE || schemaReference.getType() == XMLSchemaReference.SIMPLE_TYPE) && getDefaultRootElementType() == null){ if(hasInheritance() && isChildDescriptor()){ XMLField parentField = ((XMLDescriptor)getInheritancePolicy().getParentDescriptor()).getDefaultRootElementField(); //if this descriptor has a root element field different than it's parent set the leaf element type of the default root field if(parentField == null || (parentField !=null && defaultRootElementField !=null && !defaultRootElementField.getXPathFragment().equals(parentField.getXPathFragment()))){ setDefaultRootElementType(schemaReference.getSchemaContextAsQName(getNamespaceResolver())); } }else{ setDefaultRootElementType(schemaReference.getSchemaContextAsQName(getNamespaceResolver())); } } if(null != primaryKeyFields) { for(int x = 0, primaryKeyFieldsSize = this.primaryKeyFields.size(); x) ((ArrayList) getFields()).clone()); getObjectBuilder().initialize(session); if (hasInterfacePolicy()) { interfaceInitialization(session); } if (hasReturningPolicy()) { getReturningPolicy().initialize(session); } if (eventManager != null) { eventManager.initialize(session); } if (copyPolicy != null) { copyPolicy.initialize(session); } getInstantiationPolicy().initialize(session); if (getSchemaReference() != null) { getSchemaReference().initialize(session); } // If a Location Accessor is set on a superclass, inherit it if (getInheritancePolicyOrNull() != null && getInheritancePolicy().getParentDescriptor() != null) { XMLDescriptor d = (XMLDescriptor) getInheritancePolicy().getParentDescriptor(); locationAccessor = d.getLocationAccessor(); } if (locationAccessor != null) { locationAccessor.initializeAttributes(getJavaClass()); } } /** * INTERNAL: * XML descriptors are initialized normally, since they do * not need to be cloned by XML aggregate mappings. */ @Override public boolean requiresInitialization(AbstractSession session) { return (!isDescriptorForInterface()); } /** * Aggregates use a dummy table as default. */ @Override protected DatabaseTable extractDefaultTable() { return new DatabaseTable(); } /** * INTERNAL: * Determines the appropriate object to return from the unmarshal * call. The method will either return the object created in the * xmlReader.parse() call or an instance of Root. An Root * instance will be returned if the DOMRecord element being * unmarshalled does not equal the descriptor's default root * element. * * @return object */ @Override public Object wrapObjectInXMLRoot(UnmarshalRecord unmarshalRecord, boolean forceWrap) { String elementLocalName = unmarshalRecord.getLocalName(); String elementNamespaceUri = unmarshalRecord.getRootElementNamespaceUri(); if (forceWrap || shouldWrapObject(unmarshalRecord.getCurrentObject(), elementNamespaceUri, elementLocalName, null, unmarshalRecord.isNamespaceAware())) { Root xmlRoot = new XMLRoot(); xmlRoot.setLocalName(elementLocalName); xmlRoot.setNamespaceURI(elementNamespaceUri); xmlRoot.setObject(unmarshalRecord.getCurrentObject()); xmlRoot.setEncoding(unmarshalRecord.getEncoding()); xmlRoot.setVersion(unmarshalRecord.getVersion()); xmlRoot.setSchemaLocation(unmarshalRecord.getSchemaLocation()); xmlRoot.setNoNamespaceSchemaLocation(unmarshalRecord.getNoNamespaceSchemaLocation()); xmlRoot.setNil(unmarshalRecord.isNil()); setDeclaredTypeOnXMLRoot(xmlRoot, elementNamespaceUri, elementLocalName, unmarshalRecord.isNamespaceAware(), unmarshalRecord.getUnmarshaller()); return xmlRoot; } return unmarshalRecord.getCurrentObject(); } /** * INTERNAL: * Determines the appropriate object to return from the unmarshal * call. The method will either return the object created in the * xmlReader.parse() call or an instance of Root. An Root * instance will be returned if the DOMRecord element being * unmarshalled does not equal the descriptor's default root * element. * * @return object */ @Override public Object wrapObjectInXMLRoot(Object object, String elementNamespaceUri, String elementLocalName, String elementPrefix, boolean forceWrap, boolean isNamespaceAware, XMLUnmarshaller xmlUnmarshaller) { if (forceWrap || shouldWrapObject(object, elementNamespaceUri, elementLocalName, elementPrefix, isNamespaceAware)) { // if the DOMRecord element != descriptor's default // root element, create an Root, populate and return it Root xmlRoot = new XMLRoot(); xmlRoot.setLocalName(elementLocalName); xmlRoot.setNamespaceURI(elementNamespaceUri); xmlRoot.setObject(object); setDeclaredTypeOnXMLRoot(xmlRoot, elementNamespaceUri, elementLocalName, isNamespaceAware, xmlUnmarshaller); return xmlRoot; } return object; } /** * INTERNAL: */ @Override public Object wrapObjectInXMLRoot(Object object, String elementNamespaceUri, String elementLocalName, String elementPrefix, String encoding, String version, boolean forceWrap, boolean isNamespaceAware, XMLUnmarshaller unmarshaller) { if (forceWrap || shouldWrapObject(object, elementNamespaceUri, elementLocalName, elementPrefix, isNamespaceAware)) { // if the DOMRecord element != descriptor's default // root element, create an XMLRoot, populate and return it Root xmlRoot = new XMLRoot(); xmlRoot.setLocalName(elementLocalName); xmlRoot.setNamespaceURI(elementNamespaceUri); xmlRoot.setObject(object); xmlRoot.setEncoding(encoding); xmlRoot.setVersion(version); setDeclaredTypeOnXMLRoot(xmlRoot, elementNamespaceUri, elementLocalName, isNamespaceAware, unmarshaller); return xmlRoot; } return object; } private void setDeclaredTypeOnXMLRoot(Root xmlRoot, String elementNamespaceUri, String elementLocalName, boolean isNamespaceAware, Unmarshaller unmarshaller){ XPathQName xpathQName = new XPathQName(elementNamespaceUri, elementLocalName, isNamespaceAware); Descriptor desc = unmarshaller.getContext().getDescriptor(xpathQName); if(desc != null){ xmlRoot.setDeclaredType(desc.getJavaClass()); } } /** * INTERNAL: */ public boolean shouldWrapObject(Object object, String elementNamespaceUri, String elementLocalName, String elementPrefix, boolean isNamespaceAware) { if(resultAlwaysXMLRoot){ return true; } XMLField defaultRootField = getDefaultRootElementField(); // if the descriptor's default root element is null, we want to // create/return an XMLRoot - otherwise, we need to compare the // default root element vs. the root element in the instance doc. if (defaultRootField != null) { // resolve namespace prefix if one exists String defaultRootName = defaultRootField.getXPathFragment().getLocalName(); String defaultRootNamespaceUri = defaultRootField.getXPathFragment().getNamespaceURI(); // if the DOMRecord element == descriptor's default // root element, return the object as per usual if(isNamespaceAware){ if ((((defaultRootNamespaceUri == null) && (elementNamespaceUri == null)) || ((defaultRootNamespaceUri == null) && (elementNamespaceUri.isEmpty())) || ((elementNamespaceUri == null) && (defaultRootNamespaceUri.isEmpty())) || (((defaultRootNamespaceUri != null) && (elementNamespaceUri != null)) && (defaultRootNamespaceUri .equals(elementNamespaceUri)))) && (defaultRootName.equals(elementLocalName))) { return false; } }else{ if (defaultRootName.equals(elementLocalName)) { return false; } } } return true; } @Override public XMLField getDefaultRootElementField() { return defaultRootElementField; } /** * @return true if a new default root element field was created, else false. */ private boolean setDefaultRootElementField(String newDefaultRootElement) { if (null == newDefaultRootElement || newDefaultRootElement.isEmpty()) { setDefaultRootElementField((XMLField) null); return false; } if(getDefaultRootElementField() != null && newDefaultRootElement.equals(getDefaultRootElementField().getName())){ return false; } // create the root element xml field based on default root element name setDefaultRootElementField(new XMLField(newDefaultRootElement)); return true; } public void setDefaultRootElementField(XMLField xmlField) { defaultRootElementField = xmlField; } @Override public QName getDefaultRootElementType() { if (defaultRootElementField != null) { return defaultRootElementField.getLeafElementType(); } return null; } /** * The default root element type string will be stored until * initialization - a QName will be created and stored on the * default root element field during initialize. * */ public void setDefaultRootElementType(QName type) { if (defaultRootElementField != null) { defaultRootElementField.setLeafElementType(type); } } /** * INTERNAL: *

Indicates if the Object mapped by this descriptor is a sequenced data object * and should be marshalled accordingly. */ @Override public boolean isSequencedObject() { return sequencedObject; } public void setSequencedObject(boolean isSequenced) { this.sequencedObject = isSequenced; } @Override public boolean isWrapper() { return isWrapper; } public void setIsWrapper(boolean value) { this.isWrapper = value; } @Override public boolean isResultAlwaysXMLRoot() { return resultAlwaysXMLRoot; } @Override public void setResultAlwaysXMLRoot(boolean resultAlwaysXMLRoot) { this.resultAlwaysXMLRoot = resultAlwaysXMLRoot; } /** * INTERNAL: * Returns true if any of the mappings on this descriptor are key-based reference * mappings. */ public boolean hasReferenceMappings() { return this.hasReferenceMappings; } @Override public DatabaseField getTypedField(DatabaseField field) { XMLField foundField = (XMLField) super.getTypedField(field); if(null != foundField) { return foundField; } StringTokenizer stringTokenizer = new StringTokenizer(field.getName(), String.valueOf(XPATH_FRAGMENT_SEPARATOR)); DatabaseField typedField = getTypedField(stringTokenizer); if(null == typedField) { DatabaseMapping selfMapping = objectBuilder.getMappingForField(new XMLField(".")); if(null != selfMapping) { return selfMapping.getReferenceDescriptor().getTypedField(field); } } return typedField; } protected DatabaseField getTypedField(StringTokenizer stringTokenizer) { StringBuilder xPath = new StringBuilder(); XMLField xmlField = new XMLField(); xmlField.setNamespaceResolver(namespaceResolver); while(stringTokenizer.hasMoreElements()) { String nextToken = stringTokenizer.nextToken(); xmlField.setXPath(xPath + nextToken); xmlField.initialize(); DatabaseMapping mapping = objectBuilder.getMappingForField(xmlField); if(null == mapping) { XPathFragment xPathFragment = new XPathFragment(nextToken); if(xPathFragment.getIndexValue() > 0) { xmlField.setXPath(xPath + nextToken.substring(0, nextToken.indexOf('['))); xmlField.initialize(); mapping = objectBuilder.getMappingForField(xmlField); if(null != mapping) { if(mapping.isCollectionMapping()) { if(mapping.getContainerPolicy().isListPolicy()) { if(stringTokenizer.hasMoreElements()) { return ((XMLDescriptor) mapping.getReferenceDescriptor()).getTypedField(stringTokenizer); } else { return mapping.getField(); } } } } } } else { if(stringTokenizer.hasMoreElements()) { return ((XMLDescriptor) mapping.getReferenceDescriptor()).getTypedField(stringTokenizer); } else { return mapping.getField(); } } xPath = xPath.append(nextToken).append(XPATH_FRAGMENT_SEPARATOR); } return null; } /** * INTERNAL: * Returns this Descriptor's location accessor, if one is defined. */ @Override public AttributeAccessor getLocationAccessor() { return locationAccessor; } /** * INTERNAL: * Set this Descriptor's location accessor. */ @Override public void setLocationAccessor(AttributeAccessor value) { this.locationAccessor = value; } /** * INTERNAL: * Convert all the class-name-based settings in this Descriptor to actual class-based * settings. This method is used when converting a project that has been built * with class names to a project with classes. */ @Override public void convertClassNamesToClasses(ClassLoader classLoader){ super.convertClassNamesToClasses(classLoader); if(this.attributeGroups != null) { for(AttributeGroup next:attributeGroups.values()) { next.convertClassNamesToClasses(classLoader); } } } }