org.eclipse.persistence.oxm.XMLDescriptor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 1998, 2018 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 java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.xml.namespace.QName;
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.helper.NonSynchronizedVector;
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;
/**
* 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 static final Vector EMPTY_VECTOR = NonSynchronizedVector.newInstance(1);
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 = NonSynchronizedVector.newInstance(3);
this.mappings = NonSynchronizedVector.newInstance();
this.primaryKeyFields = null;
this.fields = NonSynchronizedVector.newInstance();
this.allFields = NonSynchronizedVector.newInstance();
this.constraintDependencies = EMPTY_VECTOR;
this.multipleTableForeignKeys = Collections.EMPTY_MAP;
this.queryKeys = Collections.EMPTY_MAP;
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.EMPTY_MAP;
this.additionalWritableMapKeyFields = Collections.EMPTY_LIST;
// 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
*/
public String getDefaultRootElement() {
if (getTables().isEmpty()) {
return null;
}
return getTables().firstElement().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
*/
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
*/
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
*/
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
*/
public NamespaceResolver getNamespaceResolver() {
return namespaceResolver;
}
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
*/
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
*/
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
*/
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
*/
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.
*/
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 Vector getPrimaryKeyFieldNames() {
if(null == primaryKeyFields) {
return new Vector(0);
}
return super.getPrimaryKeyFieldNames();
}
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
*/
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)) {
return getObjectBuilder().createRecord(null);
}
Vector nestedRows = (Vector) fieldValue;
if (nestedRows.isEmpty()) {
return getObjectBuilder().createRecord(null);
} else {
// BUG#2667762 - If the tag was empty this could be a string of whitespace.
if (!(nestedRows.firstElement() instanceof AbstractRecord)) {
return getObjectBuilder().createRecord(null);
}
return (XMLRecord) nestedRows.firstElement();
}
}
/**
* 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
*/
public Vector 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.
*/
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.
*/
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.
*/
public Vector buildDirectValuesFromFieldValue(Object fieldValue) throws DatabaseException {
if (!(fieldValue instanceof Vector)) {
Vector fieldValues = new Vector(1);
fieldValues.add(fieldValue);
return fieldValues;
}
return (Vector) fieldValue;
}
/**
* INTERNAL:
* Build the appropriate field value for the specified
* set of direct values.
* The database better be expecting a Vector.
*/
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.
*/
public Object buildFieldValueFromNestedRows(Vector nestedRows, String structureName, AbstractSession session) throws DatabaseException {
return nestedRows;
}
/**
* INTERNAL:
* A DatabaseField is built from the given field name.
*/
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.
*/
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.
*/
public void initializeAggregateInheritancePolicy(AbstractSession session) {
// do nothing, since the parent descriptor was already modified during pre-initialize
}
@Override
public void setTableNames(Vector tableNames) {
if (null != tableNames && tableNames.size() > 0) {
setDefaultRootElementField((String) tableNames.get(0));
}
super.setTableNames(tableNames);
}
/**
* INTERNAL:
* Sets the tables
*/
public void setTables(Vector theTables) {
super.setTables(theTables);
}
/**
* INTERNAL:
* Allow the descriptor to initialize any dependencies on this session.
*/
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 (Enumeration mappingsEnum = getMappings().elements(); mappingsEnum.hasMoreElements();) {
try {
DatabaseMapping mapping = (DatabaseMapping) mappingsEnum.nextElement();
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.
*/
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().elementAt(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.
*/
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(); xIndicates if the Object mapped by this descriptor is a sequenced data object
* and should be marshalled accordingly.
*/
public boolean isSequencedObject() {
return sequencedObject;
}
public void setSequencedObject(boolean isSequenced) {
this.sequencedObject = isSequenced;
}
public boolean isWrapper() {
return isWrapper;
}
public void setIsWrapper(boolean value) {
this.isWrapper = value;
}
public boolean isResultAlwaysXMLRoot() {
return resultAlwaysXMLRoot;
}
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.toString() + nextToken);
xmlField.initialize();
DatabaseMapping mapping = objectBuilder.getMappingForField(xmlField);
if(null == mapping) {
XPathFragment xPathFragment = new XPathFragment(nextToken);
if(xPathFragment.getIndexValue() > 0) {
xmlField.setXPath(xPath.toString() + 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.
*/
public AttributeAccessor getLocationAccessor() {
return locationAccessor;
}
/**
* INTERNAL:
* Set this Descriptor's location accessor.
*/
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.
* @param classLoader
*/
@Override
public void convertClassNamesToClasses(ClassLoader classLoader){
super.convertClassNamesToClasses(classLoader);
if(this.attributeGroups != null) {
for(AttributeGroup next:attributeGroups.values()) {
next.convertClassNamesToClasses(classLoader);
}
}
}
}