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, 2019 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
     */
    @Override
    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
     */
    @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 Vector getPrimaryKeyFieldNames() {
        if(null == primaryKeyFields) {
            return new Vector<>(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)) {
            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
    */
    @Override
    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.
    */
    @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 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.
     */
    @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(Vector tableNames) {
        if (null != tableNames && tableNames.size() > 0) {
            setDefaultRootElementField((String) tableNames.get(0));
        }
        super.setTableNames(tableNames);
    }

    /**
     * INTERNAL:
     * Sets the tables
     */
    @Override
    public void setTables(Vector 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 (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.
     */
    @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().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.
     */
    @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(); xIndicates 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.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.
     */
    @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.
     * @param classLoader
     */
    @Override
    public void convertClassNamesToClasses(ClassLoader classLoader){
        super.convertClassNamesToClasses(classLoader);
        if(this.attributeGroups != null) {
            for(AttributeGroup next:attributeGroups.values()) {
                next.convertClassNamesToClasses(classLoader);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy