Package org.datanucleus.api.jpa.metadata

Source Code of org.datanucleus.api.jpa.metadata.JPAMetaDataHandler

/**********************************************************************
Copyright (c) 2006 Andy Jefferson and others. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Contributors:
    ...
**********************************************************************/
package org.datanucleus.api.jpa.metadata;

import org.xml.sax.Attributes;
import org.xml.sax.EntityResolver;
import org.xml.sax.SAXException;

import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.ClassMetaData;
import org.datanucleus.metadata.ClassPersistenceModifier;
import org.datanucleus.metadata.ColumnMetaData;
import org.datanucleus.metadata.DiscriminatorMetaData;
import org.datanucleus.metadata.DiscriminatorStrategy;
import org.datanucleus.metadata.ElementMetaData;
import org.datanucleus.metadata.EmbeddedMetaData;
import org.datanucleus.metadata.EventListenerMetaData;
import org.datanucleus.metadata.FieldMetaData;
import org.datanucleus.metadata.FieldPersistenceModifier;
import org.datanucleus.metadata.FileMetaData;
import org.datanucleus.metadata.IdentityStrategy;
import org.datanucleus.metadata.IdentityType;
import org.datanucleus.metadata.InheritanceMetaData;
import org.datanucleus.metadata.InheritanceStrategy;
import org.datanucleus.metadata.JoinMetaData;
import org.datanucleus.metadata.KeyMetaData;
import org.datanucleus.metadata.MetaData;
import org.datanucleus.metadata.MetaDataManager;
import org.datanucleus.metadata.MetadataFileType;
import org.datanucleus.metadata.NullValue;
import org.datanucleus.metadata.OrderMetaData;
import org.datanucleus.metadata.PackageMetaData;
import org.datanucleus.metadata.PropertyMetaData;
import org.datanucleus.metadata.QueryLanguage;
import org.datanucleus.metadata.QueryMetaData;
import org.datanucleus.metadata.QueryResultMetaData;
import org.datanucleus.metadata.SequenceMetaData;
import org.datanucleus.metadata.TableGeneratorMetaData;
import org.datanucleus.metadata.UniqueMetaData;
import org.datanucleus.metadata.VersionMetaData;
import org.datanucleus.metadata.VersionStrategy;
import org.datanucleus.metadata.xml.AbstractMetaDataHandler;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.StringUtils;

/**
* Parser handler for JPA MetaData.
* Implements DefaultHandler and handles the extracting of MetaData for JPA
* from the XML elements/attributes. This class simply constructs the MetaData
* representation mirroring what is in the MetaData file. It has no knowledge
* of the class(es) that it represents, simply the information in the MetaData
* file. The knowledge of the classes is imposed on the representation at a
* later stage where necessary.
* <P>Operates the parse process using a Stack. MetaData components are added
* to the stack as they are encountered and created. They are then popped off
* the stack when the end element is encountered.</P>
*/
public class JPAMetaDataHandler extends AbstractMetaDataHandler
{
    /** Default package name for the entity-mappings (if any). */
    String defaultPackageName = null;

    /** Flag for whether the MetaData in this file is "metadata-complete" */
    boolean metaDataComplete = false;

    /** Default value for whether to cascade-persist. */
    boolean defaultCascadePersist = false;

    /** Whether this file is using property access. */
    boolean propertyAccess = false;

    /** Current query result entity (persistent class), during parse process. */
    String queryResultEntityName = null;

    /**
     * Constructor. Protected to prevent instantiation.
     * @param mgr the metadata manager
     * @param filename The name of the file to parse
     * @param resolver Entity Resolver to use (null if not available)
     */
    public JPAMetaDataHandler(MetaDataManager mgr, String filename, EntityResolver resolver)
    {
        super(mgr, filename, resolver);
        metadata = new FileMetaData();
        ((FileMetaData)metadata).setFilename(filename);
        ((FileMetaData)metadata).setMetaDataManager(mgr); // Parsing via this manager, so set it
        pushStack(metadata); // Start with FileMetaData on the stack
    }

    /**
     * Utility to create a new class component.
     * @param pmd The parent PackageMetaData
     * @param attrs The attributes
     * @param embeddedOnly Whether this class is embedded-only
     * @return The ClassMetaData
     */
    protected ClassMetaData newClassObject(PackageMetaData pmd, Attributes attrs, boolean embeddedOnly)
    {
        String className = getAttr(attrs, "class");
        if (className.indexOf('.') > 0)
        {
            // Strip off any package info
            className = className.substring(className.lastIndexOf('.')+1);
        }

        ClassMetaData cmd = new ClassMetaData(pmd, className);
        cmd.setEntityName(getAttr(attrs, "name"));
        cmd.setRequiresExtent(true);
        cmd.setDetachable(true);
        cmd.setPersistenceModifier(ClassPersistenceModifier.PERSISTENCE_CAPABLE);
        cmd.setEmbeddedOnly(embeddedOnly);
        cmd.setIdentityType(embeddedOnly ? IdentityType.NONDURABLE : IdentityType.APPLICATION);
        cmd.setCacheable(getAttr(attrs, "cacheable"));

        String classMetaDataComplete = getAttr(attrs, "metadata-complete");
        if (metaDataComplete || (classMetaDataComplete != null && classMetaDataComplete.equalsIgnoreCase("true")))
        {
            // Ignore any annotations since either the class or the whole file is "metadata-complete"
            cmd.setMetaDataComplete();
        }

        return cmd;
    }

    /**
     * Utility to create a new field/property component and add it to the class as required.
     * If the field/property already exists
     * @param acmd The parent class MetaData
     * @param attrs The attributes
     * @return The FieldMetaData/PropertyMetaData
     */
    protected AbstractMemberMetaData newFieldObject(AbstractClassMetaData acmd, Attributes attrs)
    {
        String fetch = getAttr(attrs, "fetch");
        String dfg = "true";
        if (fetch != null && fetch.equalsIgnoreCase("LAZY"))
        {
            dfg = "false";
        }

        AbstractMemberMetaData mmd = null;
        mmd = acmd.getMetaDataForMember(getAttr(attrs, "name"));
        if (mmd != null)
        {
            // Member exists, so add all attributes required
            if (dfg != null)
            {
                mmd.setDefaultFetchGroup(dfg.equals("true") ? true : false);
            }

            String depString = getAttr(attrs,"dependent");
            if (!StringUtils.isWhitespace(depString))
            {
                mmd.setDependent((depString.trim().equalsIgnoreCase("true") ? true : false));
            }

            mmd.setMappedBy(getAttr(attrs,"mapped-by"));

            String loadFg = getAttr(attrs,"load-fetch-group");
            if (!StringUtils.isWhitespace(loadFg))
            {
                mmd.setLoadFetchGroup(loadFg);
            }
        }
        else
        {
            if (propertyAccess)
            {
                mmd = new PropertyMetaData(acmd, getAttr(attrs,"name"));
            }
            else
            {
                mmd = new FieldMetaData(acmd, getAttr(attrs,"name"));
            }
            mmd.setPersistenceModifier(FieldPersistenceModifier.PERSISTENT.toString());
            mmd.setDefaultFetchGroup(dfg);
            mmd.setDependent(getAttr(attrs,"dependent"));
            mmd.setMappedBy(getAttr(attrs,"mapped-by"));
            mmd.setLoadFetchGroup(getAttr(attrs,"load-fetch-group"));
            acmd.addMember(mmd);
        }
        if (defaultCascadePersist)
        {
            // This file has <persistence-unit-defaults> set to cascade-persist all fields
            mmd.setCascadePersist(true);
        }

        String optionalStr = getAttr(attrs, "optional");
        if (optionalStr != null && optionalStr.equalsIgnoreCase("false"))
        {
            mmd.setNullValue(NullValue.getNullValue(optionalStr));
        }

        String orphanRemovalStr = getAttr(attrs, "orphan-removal");
        if (orphanRemovalStr != null && orphanRemovalStr.equalsIgnoreCase("true"))
        {
            mmd.setCascadeRemoveOrphans(true);
        }
        return mmd;
    }

    /**
     * Utility to create a new primary key field/property component.
     * @param acmd The parent class MetaData
     * @param attrs Attributes of the "id" element
     * @return The FieldMetaData/PropertyMetaData
     */
    protected AbstractMemberMetaData newPKFieldObject(AbstractClassMetaData acmd, Attributes attrs)
    {
        AbstractMemberMetaData mmd = null;

        mmd = acmd.getMetaDataForMember(getAttr(attrs, "name"));
        if (mmd != null)
        {
            // Member exists, so mark as PK
            mmd.setPrimaryKey(true);
        }
        else
        {
            // Create new property/field
            if (propertyAccess)
            {
                mmd = new PropertyMetaData(acmd, getAttr(attrs, "name"));
            }
            else
            {
                mmd = new FieldMetaData(acmd, getAttr(attrs, "name"));
            }
            mmd.setPersistenceModifier(FieldPersistenceModifier.PERSISTENT.toString());
            mmd.setPrimaryKey(true);
            if (defaultCascadePersist)
            {
                // This file has <persistence-unit-defaults> set to cascade-persist all fields
                mmd.setCascadePersist(true);
            }
            acmd.addMember(mmd);
        }
        return mmd;
    }

    /**
     * Utility to create a new transient field/property component.
     * @param md The parent MetaData
     * @param name Name of the transient field
     * @return The FieldMetaData/PropertyMetaData
     */
    protected AbstractMemberMetaData newTransientFieldObject(MetaData md, String name)
    {
        AbstractMemberMetaData mmd = null;
        if (propertyAccess)
        {
            mmd = new PropertyMetaData(md, name);
        }
        else
        {
            mmd = new FieldMetaData(md, name);
        }
        mmd.setNotPersistent();
        if (defaultCascadePersist)
        {
            // This file has <persistence-unit-defaults> set to cascade-persist all fields
            mmd.setCascadePersist(true);
        }
        return mmd;
    }

    /**
     * Utility to create a new embedded field/property component.
     * @param md The parent MetaData
     * @param name Name of the embedded field
     * @return The FieldMetaData/PropertyMetaData
     */
    protected AbstractMemberMetaData newEmbeddedFieldObject(MetaData md, String name)
    {
        AbstractMemberMetaData mmd = null;
        if (propertyAccess)
        {
            mmd = new PropertyMetaData(md, name);
        }
        else
        {
            mmd = new FieldMetaData(md, name);
        }
        mmd.setEmbedded(true);
        if (defaultCascadePersist)
        {
            // This file has <persistence-unit-defaults> set to cascade-persist all fields
            mmd.setCascadePersist(true);
        }
        return mmd;
    }

    /**
     * Utility to create a new field entry for a field/property in a superclass.
     * @param md The parent MetaData
     * @param attrs Attributes of the element
     * @return The FieldMetaData/PropertyMetaData
     */
    protected AbstractMemberMetaData newOverriddenFieldObject(MetaData md, Attributes attrs)
    {
        AbstractMemberMetaData mmd = null;
        if (propertyAccess)
        {
            mmd = new PropertyMetaData(md, "#UNKNOWN." + getAttr(attrs, "name"));
        }
        else
        {
            mmd = new FieldMetaData(md, "#UNKNOWN." + getAttr(attrs, "name"));
        }
        String colName = getAttr(attrs, "column");
        if (colName != null)
        {
            mmd.setColumn(colName);
        }
        mmd.setPersistenceModifier(FieldPersistenceModifier.PERSISTENT.toString());
        if (defaultCascadePersist)
        {
            // This file has <persistence-unit-defaults> set to cascade-persist all fields
            mmd.setCascadePersist(true);
        }
        return mmd;
    }

    /**
     * Utility to create a new field entry for a field/property in a superclass.
     * @param embmd The parent MetaData
     * @param attrs Attributes of the "id" element
     * @return The FieldMetaData/PropertyMetaData
     */
    protected AbstractMemberMetaData newOverriddenEmbeddedFieldObject(EmbeddedMetaData embmd, Attributes attrs)
    {
        String memberName = getAttr(attrs, "name");
        return newOverriddenEmbeddedFieldObject(embmd, memberName, getAttr(attrs, "column"));
    }

    /** Temporary variable for when processing an xxx-override so we remember the field it actually refers to */
    AbstractMemberMetaData overrideMmd = null;

    /**
     * Recursive method to process embedded member overrides.
     * @param embmd The parent MetaData
     * @param memberName The member name being overridden
     * @param columnName Column name to override it with
     * @return The FieldMetaData/PropertyMetaData
     */
    protected AbstractMemberMetaData newOverriddenEmbeddedFieldObject(EmbeddedMetaData embmd, String memberName,
            String columnName)
    {
        if (memberName.indexOf('.') > 0)
        {
            int position = memberName.indexOf('.');
            String baseMemberName = memberName.substring(0, position);
            String nestedMemberName = memberName.substring(position+1);

            AbstractMemberMetaData mmd = null;
            if (propertyAccess)
            {
                mmd = new PropertyMetaData(embmd, baseMemberName);
            }
            else
            {
                mmd = new FieldMetaData(embmd, baseMemberName);
            }

            EmbeddedMetaData nestedEmbmd = new EmbeddedMetaData();
            nestedEmbmd.setParent(mmd);
            mmd.setEmbeddedMetaData(nestedEmbmd);

            AbstractMemberMetaData nestedEmbMmd = newOverriddenEmbeddedFieldObject(nestedEmbmd, nestedMemberName, columnName);
            nestedEmbmd.addMember(nestedEmbMmd);
            overrideMmd = nestedEmbMmd;
            return mmd;
        }
        else
        {
            AbstractMemberMetaData mmd = null;
            if (propertyAccess)
            {
                mmd = new PropertyMetaData(embmd, memberName);
            }
            else
            {
                mmd = new FieldMetaData(embmd, memberName);
            }
            mmd.setParent(embmd);

            if (columnName != null)
            {
                mmd.setColumn(columnName);
            }
            mmd.setPersistenceModifier(FieldPersistenceModifier.PERSISTENT.toString());
            if (defaultCascadePersist)
            {
                // This file has <persistence-unit-defaults> set to cascade-persist all fields
                mmd.setCascadePersist(true);
            }
            overrideMmd = mmd;
            return mmd;
        }
    }

    /**
     * Handler method called at the start of an element.
     * @param uri URI of the tag
     * @param localName Local name
     * @param qName Element name
     * @param attrs Attributes for this element
     * @throws SAXException in parsing errors
     */
    public void startElement(String uri, String localName, String qName, Attributes attrs)
    throws SAXException
    {
        if (localName.length()<1)
        {
            localName = qName;
        }
        try
        {
            if (localName.equals("entity-mappings"))
            {
                FileMetaData filemd = (FileMetaData)getStack();
                filemd.setType(MetadataFileType.JPA_MAPPING_FILE);
            }
            else if (localName.equals("description"))
            {
                // Of no practical use so ignored
            }
            else if (localName.equals("persistence-unit-metadata"))
            {
                // Nothing to do - we use subelements
            }
            else if (localName.equals("xml-mapping-metadata-complete"))
            {
                // All classes in the file are complete without any annotations
                metaDataComplete = true;
            }
            else if (localName.equals("persistence-unit-defaults"))
            {
                // Nothing to do - we use subelements
            }
            else if (localName.equals("package"))
            {
                // Processed in endElement()
            }
            else if (localName.equals("schema"))
            {
                // Processed in endElement()
            }
            else if (localName.equals("catalog"))
            {
                // Processed in endElement()
            }
            else if (localName.equals("access"))
            {
                // Processed in endElement()
            }
            else if (localName.equals("column-name"))
            {
                // Processed in endElement()
            }
            else if (localName.equals("sequence-generator"))
            {
                // Find the package for this sequence
                PackageMetaData pmd = null;
                FileMetaData filemd = (FileMetaData)metadata;
                if (defaultPackageName != null)
                {
                    pmd = filemd.getPackage(defaultPackageName);
                }
                else
                {
                    if (filemd.getNoOfPackages() > 0)
                    {
                        pmd = filemd.getPackage(0);
                    }
                    else
                    {
                        // Add a dummy (root) package to hold our sequences since no default package name set
                        pmd = filemd.newPackageMetadata("");
                    }
                }
                String initValue = getAttr(attrs, "initial-value");
                if (StringUtils.isWhitespace(initValue))
                {
                    initValue = "1"; // JPA default
                }
                String allocSize = getAttr(attrs, "allocation-size");
                if (StringUtils.isWhitespace(allocSize))
                {
                    allocSize = "50"; // JPA default
                }
                SequenceMetaData seqmd = pmd.newSequenceMetadata(getAttr(attrs, "name"), null);
                String datastoreSeqName = getAttr(attrs, "sequence-name");
                if (StringUtils.isWhitespace(datastoreSeqName))
                {
                    datastoreSeqName = seqmd.getName();
                }
                seqmd.setDatastoreSequence(datastoreSeqName);
                seqmd.setInitialValue(initValue);
                seqmd.setAllocationSize(allocSize);
            }
            else if (localName.equals("table-generator"))
            {
                // Find the package for this table generator
                PackageMetaData pmd = null;
                FileMetaData filemd = (FileMetaData)metadata;
                if (defaultPackageName != null)
                {
                    pmd = filemd.getPackage(defaultPackageName);
                }
                else
                {
                    if (filemd.getNoOfPackages() > 0)
                    {
                        pmd = filemd.getPackage(0);
                    }
                    else
                    {
                        // Add a dummy (root) package to hold our sequences since no default package name set
                        pmd = filemd.newPackageMetadata("");
                    }
                }
                TableGeneratorMetaData tgmd = pmd.newTableGeneratorMetadata(getAttr(attrs, "name"));
                tgmd.setTableName(getAttr(attrs, "table"));
                tgmd.setCatalogName(getAttr(attrs, "catalog"));
                tgmd.setSchemaName(getAttr(attrs, "schema"));
                tgmd.setPKColumnName(getAttr(attrs, "pk-column-name"));
                tgmd.setPKColumnValue(getAttr(attrs, "pk-column-value"));
                tgmd.setValueColumnName(getAttr(attrs, "value-column-name"));
                tgmd.setInitialValue(getAttr(attrs, "initial-value"));
                tgmd.setAllocationSize(getAttr(attrs, "allocation-size"));
            }
            else if (localName.equals("named-query"))
            {
                // Named JPQL query
                MetaData md = getStack();
                if (md instanceof FileMetaData)
                {
                    FileMetaData filemd = (FileMetaData)md;
                    QueryMetaData qmd = filemd.newQueryMetadata(getAttr(attrs, "name"));
                    qmd.setLanguage(QueryLanguage.JPQL.toString());
                    pushStack(qmd);
                }
                else if (md instanceof ClassMetaData)
                {
                    ClassMetaData cmd = (ClassMetaData)md;
                    QueryMetaData qmd = new QueryMetaData(getAttr(attrs, "name"));
                    qmd.setLanguage(QueryLanguage.JPQL.toString());
                    cmd.addQuery(qmd);
                    pushStack(qmd);
                }
            }
            else if (localName.equals("named-native-query"))
            {
                // Named SQL query
                MetaData md = getStack();
                if (md instanceof FileMetaData)
                {
                    FileMetaData filemd = (FileMetaData)md;
                    QueryMetaData qmd = filemd.newQueryMetadata(getAttr(attrs, "name"));
                    qmd.setLanguage(QueryLanguage.SQL.toString());
                    qmd.setResultClass(getAttr(attrs, "result-class"));
                    qmd.setResultMetaDataName(getAttr(attrs, "result-set-mapping"));
                    pushStack(qmd);
                }
                else if (md instanceof ClassMetaData)
                {
                    ClassMetaData cmd = (ClassMetaData)md;
                    QueryMetaData qmd = new QueryMetaData(getAttr(attrs, "name"));
                    qmd.setLanguage(QueryLanguage.SQL.toString());
                    qmd.setResultClass(getAttr(attrs, "result-class"));
                    qmd.setResultMetaDataName(getAttr(attrs, "result-set-mapping"));
                    cmd.addQuery(qmd);
                    pushStack(qmd);
                }
            }
            else if (localName.equals("sql-result-set-mapping"))
            {
                MetaData md = getStack();
                if (md instanceof FileMetaData)
                {
                    FileMetaData filemd = (FileMetaData)md;
                    QueryResultMetaData qrmd = filemd.newQueryResultMetadata(getAttr(attrs, "name"));
                    pushStack(qrmd);
                }
                else if (md instanceof ClassMetaData)
                {
                    ClassMetaData cmd = (ClassMetaData)md;
                    QueryResultMetaData qrmd = new QueryResultMetaData(getAttr(attrs, "name"));
                    cmd.addQueryResultMetaData(qrmd);
                    pushStack(qrmd);
                }
            }
            else if (localName.equals("entity-result"))
            {
                // Add an entity (persistent class) mapping
                QueryResultMetaData qrmd = (QueryResultMetaData)getStack();
                queryResultEntityName = getAttr(attrs, "entity-class"); // Save this for any field-result that arrives
                qrmd.addPersistentTypeMapping(queryResultEntityName,
                    null, // No field-column mappings info at this point
                    getAttr(attrs, "discriminator-column"));
            }
            else if (localName.equals("field-result"))
            {
                // Add a field-column mapping for the entity (persistent class)
                QueryResultMetaData qrmd = (QueryResultMetaData)getStack();
                qrmd.addMappingForPersistentTypeMapping(queryResultEntityName,
                    getAttr(attrs, "name"), getAttr(attrs, "column"));
            }
            else if (localName.equals("column-result"))
            {
                // Add a scalar column mapping
                QueryResultMetaData qrmd = (QueryResultMetaData)getStack();
                qrmd.addScalarColumn(getAttr(attrs, "name"));
            }
            else if (localName.equals("mapped-superclass"))
            {
                // New entity for this package
                FileMetaData filemd = (FileMetaData)getStack();
                String className = getAttr(attrs, "class");
                String packageName = null;
                if (className.indexOf('.') > 0)
                {
                    // Fully-qualified so use package name from class
                    packageName = className.substring(0, className.lastIndexOf('.'));
                }
                PackageMetaData pmd = null;
                if (packageName != null)
                {
                    pmd = filemd.getPackage(packageName);
                }
                if (pmd == null)
                {
                    if (packageName != null)
                    {
                        // Class fully qualified so add its package
                        pmd = filemd.newPackageMetadata(packageName);
                    }
                    else if (defaultPackageName != null)
                    {
                        // Use default package for entity-mappings
                        pmd = filemd.getPackage(defaultPackageName);
                    }
                    else
                    {
                        // Add root package
                        pmd = filemd.newPackageMetadata("");
                    }
                }

                ClassMetaData cmd = newClassObject(pmd, attrs, false);
                pmd.addClass(cmd);

                // Set to use "subclass-table" since all subclasses inherit these fields
                InheritanceMetaData inhmd = new InheritanceMetaData();
                inhmd.setStrategy(InheritanceStrategy.SUBCLASS_TABLE);
                cmd.setInheritanceMetaData(inhmd);

                pushStack(cmd);
            }
            else if (localName.equals("query"))
            {
                // Processed in endElement()
            }
            else if (localName.equals("entity"))
            {
                // New entity for this package
                FileMetaData filemd = (FileMetaData)getStack();
                String className = getAttr(attrs, "class");
                String packageName = null;
                if (className.indexOf('.') > 0)
                {
                    // Fully-qualified so use package name from class
                    packageName = className.substring(0, className.lastIndexOf('.'));
                }
                PackageMetaData pmd = null;
                if (packageName != null)
                {
                    pmd = filemd.getPackage(packageName);
                }
                if (pmd == null)
                {
                    if (packageName != null)
                    {
                        // Class fully qualified so add its package
                        pmd = filemd.newPackageMetadata(packageName);
                    }
                    else if (defaultPackageName != null)
                    {
                        // Use default package for entity-mappings
                        pmd = filemd.getPackage(defaultPackageName);
                    }
                    else
                    {
                        // Add root package
                        pmd = filemd.newPackageMetadata("");
                    }
                }

                ClassMetaData cmd = newClassObject(pmd, attrs, false);
                pmd.addClass(cmd);

                pushStack(cmd);
            }
            else if (localName.equals("embeddable"))
            {
                // New embedded-only entity for this package
                FileMetaData filemd = (FileMetaData)getStack();
                String className = getAttr(attrs, "class");
                String packageName = null;
                if (className.indexOf('.') > 0)
                {
                    // Fully-qualified so use package name from class
                    packageName = className.substring(0, className.lastIndexOf('.'));
                }
                PackageMetaData pmd = null;
                if (packageName != null)
                {
                    pmd = filemd.getPackage(packageName);
                }
                if (pmd == null)
                {
                    if (packageName != null)
                    {
                        // Class fully qualified so add its package
                        pmd = filemd.newPackageMetadata(packageName);
                    }
                    else if (defaultPackageName != null)
                    {
                        // Use default package for entity-mappings
                        pmd = filemd.getPackage(defaultPackageName);
                    }
                    else
                    {
                        // Add root package
                        pmd = filemd.newPackageMetadata("");
                    }
                }

                ClassMetaData cmd = newClassObject(pmd, attrs, true);
                pmd.addClass(cmd);

                pushStack(cmd);
            }
            else if (localName.equals("attributes"))
            {
                // Nothing to do since is just a holder of other elements
            }
            else if (localName.equals("embeddable-attributes"))
            {
                // Nothing to do since is just a holder of other elements
            }
            else if (localName.equals("id-class"))
            {
                // Identity class
                ClassMetaData cmd = (ClassMetaData)getStack();
                cmd.setObjectIdClass(getAttr(attrs, "class"));
            }
            else if (localName.equals("inheritance"))
            {
                // Inheritance - only for root class
                ClassMetaData cmd = (ClassMetaData)getStack();
                String strategy = getAttr(attrs, "strategy");
                String strategyType = null;
                if (strategy != null)
                {
                    if (strategy.equalsIgnoreCase("JOINED"))
                    {
                        strategyType = InheritanceStrategy.NEW_TABLE.toString();
                    }
                    else if (strategy.equalsIgnoreCase("TABLE_PER_CLASS"))
                    {
                        strategyType = InheritanceStrategy.COMPLETE_TABLE.toString();
                    }
                }
                else
                {
                    // SINGLE_TABLE (default), so implies nothing needs setting since thats the default
                }
                InheritanceMetaData inhmd = new InheritanceMetaData();
                inhmd.setStrategy(strategyType);
                if (strategy != null)
                {
                    inhmd.setStrategyForTree(strategy.toUpperCase());
                }
                cmd.setInheritanceMetaData(inhmd);
            }
            else if (localName.equals("table"))
            {
                // Table for this entity
                ClassMetaData cmd = (ClassMetaData)getStack();
                cmd.setCatalog(getAttr(attrs, "catalog"));
                cmd.setSchema(getAttr(attrs, "schema"));
                cmd.setTable(getAttr(attrs, "name"));
            }
            else if (localName.equals("secondary-table"))
            {
                // Join for this entity
                ClassMetaData cmd = (ClassMetaData)getStack();
                JoinMetaData joinmd = new JoinMetaData();
                joinmd.setTable(getAttr(attrs, "name"));
                joinmd.setCatalog(getAttr(attrs, "catalog"));
                joinmd.setSchema(getAttr(attrs, "schema"));
                cmd.addJoin(joinmd);
                pushStack(joinmd);
            }
            else if (localName.equals("primary-key-join-column"))
            {
                MetaData md = getStack();
                if (md instanceof ClassMetaData)
                {
                    // Join columns between PK of subclass table and PK of base class table
                    ClassMetaData cmd = (ClassMetaData)md;
                    ColumnMetaData colmd = new ColumnMetaData();
                    colmd.setName(getAttr(attrs, "name"));
                    colmd.setTarget(getAttr(attrs, "referenced-column-name"));
                    String columnDdl = getAttr(attrs, "column-definition");
                    if (columnDdl != null)
                    {
                        colmd.setColumnDdl(columnDdl);
                    }
                    InheritanceMetaData inhmd = cmd.getInheritanceMetaData();
                    if (inhmd == null)
                    {
                        inhmd = new InheritanceMetaData();
                        cmd.setInheritanceMetaData(inhmd);
                    }
                    JoinMetaData inhJoinmd = inhmd.getJoinMetaData();
                    if (inhJoinmd == null)
                    {
                        inhJoinmd = new JoinMetaData();
                    }
                    inhJoinmd.addColumn(colmd);
                }
                else if (md instanceof JoinMetaData)
                {
                    // Join columns between PK of secondary table and PK of primary table
                    JoinMetaData joinmd = (JoinMetaData)md;
                    ColumnMetaData colmd = new ColumnMetaData();
                    colmd.setName(getAttr(attrs, "name"));
                    colmd.setTarget(getAttr(attrs, "referenced-column-name"));
                    joinmd.addColumn(colmd);
                }
            }
            else if (localName.equals("id"))
            {
                // Identity field
                ClassMetaData cmd = (ClassMetaData)getStack();
                AbstractMemberMetaData mmd = newPKFieldObject(cmd, attrs);

                pushStack(mmd);
            }
            else if (localName.equals("embedded-id"))
            {
                // Embedded identity field
                ClassMetaData cmd = (ClassMetaData)getStack();
                AbstractMemberMetaData mmd = newPKFieldObject(cmd, attrs);

                pushStack(mmd);
            }
            else if (localName.equals("basic"))
            {
                // Basic field
                AbstractClassMetaData cmd = (AbstractClassMetaData)getStack();
                AbstractMemberMetaData mmd = newFieldObject(cmd, attrs);

                pushStack(mmd);
            }
            else if (localName.equals("lob"))
            {
                AbstractMemberMetaData fmd = (AbstractMemberMetaData)getStack();
                fmd.setStoreInLob(); // Just mark it as to be stored in a "lob" and let the MetaData sort it out
            }
            else if (localName.equals("enumerated"))
            {
                // Processed elsewhere
            }
            else if (localName.equals("temporal"))
            {
                // Processed elsewhere
            }
            else if (localName.equals("transient"))
            {
                // Transient field
                ClassMetaData cmd = (ClassMetaData)getStack();
                AbstractMemberMetaData mmd = newTransientFieldObject(cmd, getAttr(attrs, "name"));
                cmd.addMember(mmd);

                pushStack(mmd);
            }
            else if (localName.equals("embedded"))
            {
                // Embedded field
                AbstractClassMetaData cmd = (AbstractClassMetaData)getStack();
                AbstractMemberMetaData mmd = newEmbeddedFieldObject(cmd, getAttr(attrs, "name"));
                cmd.addMember(mmd);
                mmd.setEmbedded(true);

                pushStack(mmd);
            }
            else if (localName.equals("one-to-many"))
            {
                // 1-N field
                ClassMetaData cmd = (ClassMetaData)getStack();
                AbstractMemberMetaData mmd = newFieldObject(cmd, attrs);
                String targetEntityName = getAttr(attrs, "target-entity");
                if (!StringUtils.isWhitespace(targetEntityName))
                {
                    mmd.setTargetClassName(targetEntityName);
                }
                mmd.setOrdered(); // Mark as ordered so we know we're using JPA
                String jpaLevel = mgr.getNucleusContext().getPersistenceConfiguration().getStringProperty("datanucleus.jpa.level");
                if (mmd.getMappedBy() == null && mmd.getJoinMetaData() == null &&
                    jpaLevel.equalsIgnoreCase("JPA1"))
                {
                    // JPA1 : 1-N uni with no join specified (yet) so add one (see JPA1 spec [9.1.24])
                    mmd.setJoinMetaData(new JoinMetaData());
                }

                pushStack(mmd);
            }
            else if (localName.equals("one-to-one"))
            {
                // 1-1 field
                ClassMetaData cmd = (ClassMetaData)getStack();
                AbstractMemberMetaData mmd = newFieldObject(cmd, attrs);
                String targetEntityName = getAttr(attrs, "target-entity");
                if (!StringUtils.isWhitespace(targetEntityName))
                {
                    mmd.setTargetClassName(targetEntityName);
                }

                pushStack(mmd);
            }
            else if (localName.equals("many-to-one"))
            {
                // N-1 field
                ClassMetaData cmd = (ClassMetaData)getStack();
                AbstractMemberMetaData mmd = newFieldObject(cmd, attrs);
                String targetEntityName = getAttr(attrs, "target-entity");
                if (!StringUtils.isWhitespace(targetEntityName))
                {
                    mmd.setTargetClassName(targetEntityName);
                }

                pushStack(mmd);
            }
            else if (localName.equals("many-to-many"))
            {
                // M-N field
                ClassMetaData cmd = (ClassMetaData)getStack();
                AbstractMemberMetaData mmd = newFieldObject(cmd, attrs);
                String targetEntityName = getAttr(attrs, "target-entity");
                if (!StringUtils.isWhitespace(targetEntityName))
                {
                    mmd.setTargetClassName(targetEntityName);
                }
                mmd.setOrdered(); // Mark as ordered so we know we're using JPA
                if (mmd.getMappedBy() == null && mmd.getJoinMetaData() == null)
                {
                    // M-N and no join specified (yet) so add one
                    mmd.setJoinMetaData(new JoinMetaData());
                }

                pushStack(mmd);
            }
            else if (localName.equals("element-collection"))
            {
                // Element collection for this field (1-N using non-PC elements)
                ClassMetaData cmd = (ClassMetaData)getStack();
                AbstractMemberMetaData mmd = newFieldObject(cmd, attrs);
                JoinMetaData joinmd = new JoinMetaData();
                mmd.setJoinMetaData(joinmd);
                pushStack(joinmd); // Use join so we can distinguish "element-collection"
            }
            else if (localName.equals("collection-table"))
            {
                // Collection table for this field (1-N using non-PC elements)
                JoinMetaData joinmd = (JoinMetaData)getStack();
                joinmd.setTable(getAttr(attrs, "name"));
                joinmd.setCatalog(getAttr(attrs, "catalog"));
                joinmd.setSchema(getAttr(attrs, "schema"));
            }
            else if (localName.equals("map-key"))
            {
                // Key of a Map (field/property of the value class)
                MetaData md = getStack();
                if (md instanceof AbstractMemberMetaData)
                {
                    AbstractMemberMetaData fmd = (AbstractMemberMetaData)getStack();
                    String mappedByFieldName = getAttr(attrs, "name");
                    if (StringUtils.isWhitespace(mappedByFieldName))
                    {
                        mappedByFieldName = "#PK"; // Special value understood by MapMetaData.populate()
                    }
                    KeyMetaData keymd = fmd.getKeyMetaData();
                    if (keymd == null)
                    {
                        keymd = new KeyMetaData();
                        fmd.setKeyMetaData(keymd);
                    }
                    keymd.setMappedBy(mappedByFieldName);
                }
                else if (md instanceof JoinMetaData)
                {
                    // Map<NonPC, NonPC> defining the key
                    JoinMetaData joinmd = (JoinMetaData)md;
                    AbstractMemberMetaData mmd = (AbstractMemberMetaData)joinmd.getParent();
                    String mappedByFieldName = getAttr(attrs, "name");
                    if (StringUtils.isWhitespace(mappedByFieldName))
                    {
                        mappedByFieldName = "#PK"; // Special value understood by MapMetaData.populate()
                    }
                    KeyMetaData keymd = mmd.getKeyMetaData();
                    if (keymd == null)
                    {
                        keymd = new KeyMetaData();
                        mmd.setKeyMetaData(keymd);
                    }
                    keymd.setMappedBy(mappedByFieldName);
                }
            }
            else if (localName.equals("order-by"))
            {
                // Processed in endElement()
            }
            else if (localName.equals("order-column"))
            {
                AbstractMemberMetaData fmd = (AbstractMemberMetaData)getStack();
                String columnName = getAttr(attrs, "name");
                OrderMetaData ordermd = new OrderMetaData();
                ordermd.setColumnName(columnName);

                ColumnMetaData colmd = new ColumnMetaData();
                colmd.setName(getAttr(attrs, "name"));
                colmd.setAllowsNull(getAttr(attrs, "nullable"));
                colmd.setInsertable(getAttr(attrs, "insertable"));
                colmd.setUpdateable(getAttr(attrs, "updatable"));
                String columnDdl = getAttr(attrs, "column-definition");
                if (columnDdl != null)
                {
                    colmd.setColumnDdl(columnDdl);
                }
                ordermd.addColumn(colmd);
                fmd.setOrderMetaData(ordermd);
            }
            else if (localName.equals("cascade"))
            {
                // Do nothing
            }
            else if (localName.equals("cascade-type"))
            {
                // Handled in elements below
            }
            else if (localName.equals("cascade-all"))
            {
                AbstractMemberMetaData mmd = (AbstractMemberMetaData)getStack();
                mmd.setCascadePersist(true);
                mmd.setCascadeUpdate(true);
                mmd.setCascadeDelete(true);
                mmd.setCascadeRefresh(true);
            }
            else if (localName.equals("cascade-persist"))
            {
                MetaData md = getStack();
                if (md instanceof AbstractMemberMetaData)
                {
                    AbstractMemberMetaData mmd = (AbstractMemberMetaData)md;
                    mmd.setCascadePersist(true);
                }
                else if (md instanceof FileMetaData)
                {
                    // Specified at <persistence-unit-defaults>
                    defaultCascadePersist = true;
                }
            }
            else if (localName.equals("cascade-merge"))
            {
                AbstractMemberMetaData mmd = (AbstractMemberMetaData)getStack();
                mmd.setCascadeUpdate(true);
            }
            else if (localName.equals("cascade-remove"))
            {
                AbstractMemberMetaData mmd = (AbstractMemberMetaData)getStack();
                mmd.setCascadeDelete(true);
            }
            else if (localName.equals("cascade-refresh"))
            {
                AbstractMemberMetaData mmd = (AbstractMemberMetaData)getStack();
                mmd.setCascadeRefresh(true);
            }
            else if (localName.equals("version"))
            {
                if (getStack() instanceof ClassMetaData)
                {
                    // Version field
                    ClassMetaData cmd = (ClassMetaData)getStack();
                    AbstractMemberMetaData mmd = newFieldObject(cmd, attrs);

                    // Tag this field as the version field
                    VersionMetaData vermd = cmd.newVersionMetadata();
                    vermd.setStrategy(VersionStrategy.VERSION_NUMBER).setFieldName(mmd.getName());

                    pushStack(mmd);
                }
            }
            else if (localName.equals("discriminator-value"))
            {
                // Processed in endElement()
            }
            else if (localName.equals("discriminator-column"))
            {
                ClassMetaData cmd = (ClassMetaData)getStack();
                InheritanceMetaData inhmd = cmd.getInheritanceMetaData();
                if (inhmd == null)
                {
                    // Add an empty inheritance specification
                    inhmd = new InheritanceMetaData();
                    cmd.setInheritanceMetaData(inhmd);
                }
                DiscriminatorMetaData dismd = inhmd.getDiscriminatorMetaData();
                if (dismd == null)
                {
                    // User hasn't specified discriminator value so use "provider-specific function" (JPA 9.1.3.1) - what a joke spec
                    dismd = inhmd.newDiscriminatorMetadata();
                    dismd.setStrategy(DiscriminatorStrategy.VALUE_MAP);
                    dismd.setValue(cmd.getFullClassName()); // Default to class name as value unless set
                    dismd.setIndexed("true");
                }
                String jdbcType = null;
                String discType = getAttr(attrs, "discriminator-type");
                if (discType != null)
                {
                    if (discType.equalsIgnoreCase("STRING"))
                    {
                        jdbcType = "VARCHAR";
                    }
                    else if (discType.equalsIgnoreCase("CHAR"))
                    {
                        jdbcType = "CHAR";
                    }
                    else if (discType.equalsIgnoreCase("INTEGER"))
                    {
                        jdbcType = "INTEGER";
                    }
                }
                ColumnMetaData colmd = new ColumnMetaData();
                colmd.setName(getAttr(attrs, "name"));
                colmd.setJdbcType(jdbcType);
                colmd.setLength(getAttr(attrs, "length"));
                String columnDdl = getAttr(attrs, "column-definition");
                if (columnDdl != null)
                {
                    colmd.setColumnDdl(columnDdl);
                }
                dismd.setColumnMetaData(colmd);
            }
            else if (localName.equals("generated-value"))
            {
                // generated value for this field
                AbstractMemberMetaData fmd = (AbstractMemberMetaData)getStack();
                String strategy = getAttr(attrs, "strategy");
                if (strategy != null)
                {
                    if (strategy.equalsIgnoreCase("auto"))
                    {
                        fmd.setValueStrategy(IdentityStrategy.NATIVE);
                    }
                    else if (strategy.equalsIgnoreCase("table"))
                    {
                        fmd.setValueStrategy(IdentityStrategy.INCREMENT);
                    }
                    else if (strategy.equalsIgnoreCase("sequence"))
                    {
                        fmd.setValueStrategy(IdentityStrategy.SEQUENCE);
                    }
                    else if (strategy.equalsIgnoreCase("identity"))
                    {
                        fmd.setValueStrategy(IdentityStrategy.IDENTITY);
                    }
                }
                fmd.setValueGeneratorName(getAttr(attrs, "generator"));
            }
            else if (localName.equals("join-table"))
            {
                // Join table for this field
                AbstractMemberMetaData fmd = (AbstractMemberMetaData)getStack();
                JoinMetaData joinmd = new JoinMetaData();
                String tableName = getAttr(attrs, "name");
                String schemaName = getAttr(attrs, "schema");
                String catalogName = getAttr(attrs, "catalog");

                fmd.setJoinMetaData(joinmd);
                if (!StringUtils.isWhitespace(tableName))
                {
                    fmd.setTable(tableName);
                }
                if (!StringUtils.isWhitespace(schemaName))
                {
                    fmd.setSchema(schemaName);
                }
                if (!StringUtils.isWhitespace(catalogName))
                {
                    fmd.setSchema(catalogName);
                }
                pushStack(joinmd);
            }
            else if (localName.equals("column"))
            {
                // Column for the current field
                MetaData md = getStack();
                ColumnMetaData colmd = new ColumnMetaData();
                colmd.setName(getAttr(attrs, "name"));
                colmd.setLength(getAttr(attrs, "length"));
                colmd.setScale(getAttr(attrs, "scale"));
                colmd.setAllowsNull(getAttr(attrs, "nullable"));
                colmd.setInsertable(getAttr(attrs, "insertable"));
                colmd.setUpdateable(getAttr(attrs, "updatable"));
                colmd.setUnique(getAttr(attrs, "unique"));
                String columnDdl = getAttr(attrs, "column-definition");
                if (columnDdl != null)
                {
                    colmd.setColumnDdl(columnDdl);
                }
                if (md instanceof AbstractMemberMetaData)
                {
                    AbstractMemberMetaData fmd = (AbstractMemberMetaData)md;
                    if (overrideMmd != null)
                    {
                        // Actually the column of an [attribute/association]-override
                        fmd = overrideMmd;
                    }

                    fmd.addColumn(colmd);
                    String table = getAttr(attrs, "table");
                    if (!StringUtils.isWhitespace(table))
                    {
                        // Using secondary table
                        fmd.setTable(table);
                    }
                }
                else if (md instanceof JoinMetaData)
                {
                    // <element-collection>
                    //     <column .../>
                    // </element-collection>
                    JoinMetaData joinmd = (JoinMetaData)md;
                    AbstractMemberMetaData mmd = (AbstractMemberMetaData)joinmd.getParent();
                    ElementMetaData elemmd = mmd.getElementMetaData();
                    if (elemmd == null)
                    {
                        elemmd = new ElementMetaData();
                        mmd.setElementMetaData(elemmd);
                    }
                    elemmd.addColumn(colmd);
                }
            }
            else if (localName.equals("map-key-column"))
            {
                // Column for the current field
                MetaData md = getStack();
               
                ColumnMetaData colmd = new ColumnMetaData();
                colmd.setName(getAttr(attrs, "name"));
                if (getAttr(attrs, "precision")!=null)
                {
                    colmd.setLength(getAttr(attrs, "precision"));
                }
                else
                {
                    colmd.setLength(getAttr(attrs, "length"));
                }
                colmd.setScale(getAttr(attrs, "scale"));
                colmd.setAllowsNull(getAttr(attrs, "nullable"));
                colmd.setInsertable(getAttr(attrs, "insertable"));
                colmd.setUpdateable(getAttr(attrs, "updatable"));
                colmd.setUnique(getAttr(attrs, "unique"));
                String columnDdl = getAttr(attrs, "column-definition");
                if (columnDdl != null)
                {
                    colmd.setColumnDdl(columnDdl);
                }
                AbstractMemberMetaData mmd = (AbstractMemberMetaData)md;
                KeyMetaData keymd = mmd.getKeyMetaData();
                if (keymd == null)
                {
                    keymd = new KeyMetaData();
                    mmd.setKeyMetaData(keymd);
                }
                keymd.addColumn(colmd);
            }
            else if (localName.equals("join-column"))
            {
                MetaData md = getStack();
                if (md instanceof JoinMetaData)
                {
                    JoinMetaData joinmd = (JoinMetaData)md;
                    ColumnMetaData colmd = new ColumnMetaData();
                    colmd.setName(getAttr(attrs, "name"));
                    colmd.setTarget(getAttr(attrs, "referenced-column-name"));
                    colmd.setAllowsNull(getAttr(attrs, "nullable"));
                    colmd.setInsertable(getAttr(attrs, "insertable"));
                    colmd.setUpdateable(getAttr(attrs, "updatable"));
                    colmd.setUnique(getAttr(attrs, "unique"));
                    String columnDdl = getAttr(attrs, "column-definition");
                    if (columnDdl != null)
                    {
                        colmd.setColumnDdl(columnDdl);
                    }
                    joinmd.addColumn(colmd);
                }
                else if (md instanceof AbstractMemberMetaData)
                {
                    // N-1, 1-1, 1-N (FK). Just set <column> for the field. Is this correct for 1-N FK ?
                    AbstractMemberMetaData fmd = (AbstractMemberMetaData)md;
                    ColumnMetaData colmd = new ColumnMetaData();
                    colmd.setName(getAttr(attrs, "name"));
                    colmd.setTarget(getAttr(attrs, "referenced-column-name"));
                    colmd.setAllowsNull(getAttr(attrs, "nullable"));
                    colmd.setInsertable(getAttr(attrs, "insertable"));
                    colmd.setUpdateable(getAttr(attrs, "updatable"));
                    colmd.setUnique(getAttr(attrs, "unique"));
                    fmd.addColumn(colmd);
                }
            }
            else if (localName.equals("inverse-join-column"))
            {
                MetaData md = getStack();
                if (md instanceof JoinMetaData)
                {
                    // Join table column that is FK to the element table
                    JoinMetaData joinmd = (JoinMetaData)md;
                    ElementMetaData elemmd = null;
                    AbstractMemberMetaData fmd = (AbstractMemberMetaData)joinmd.getParent();
                    if (fmd.getElementMetaData() != null)
                    {
                        elemmd = fmd.getElementMetaData();
                    }
                    else
                    {
                        elemmd = new ElementMetaData();
                        fmd.setElementMetaData(elemmd);
                    }
                    ColumnMetaData colmd = new ColumnMetaData();
                    colmd.setName(getAttr(attrs, "name"));
                    colmd.setTarget(getAttr(attrs, "referenced-column-name"));
                    colmd.setAllowsNull(getAttr(attrs, "nullable"));
                    colmd.setInsertable(getAttr(attrs, "insertable"));
                    colmd.setUpdateable(getAttr(attrs, "updatable"));
                    colmd.setUnique(getAttr(attrs, "unique"));
                    elemmd.addColumn(colmd);
                }
            }
            else if (localName.equals("unique-constraint"))
            {
                MetaData md = getStack();
                if (md instanceof AbstractClassMetaData)
                {
                    // Unique constraint on primary table
                    AbstractClassMetaData cmd = (AbstractClassMetaData)md;
                    UniqueMetaData unimd = new UniqueMetaData();
                    unimd.setTable(cmd.getTable()); // Columns are in subelement
                    cmd.addUniqueConstraint(unimd);
                    pushStack(unimd);
                }
                else if (md instanceof JoinMetaData)
                {
                    // Unique constraint on secondary table or join table
                    JoinMetaData joinmd = (JoinMetaData)md;
                    UniqueMetaData unimd = new UniqueMetaData();
                    joinmd.setUniqueMetaData(unimd);
                    pushStack(unimd);
                }
            }
            else if (localName.equals("entity-listeners"))
            {
                // Nothing to add at this point
            }
            else if (localName.equals("entity-listener"))
            {
                MetaData md = getStack();
                EventListenerMetaData elmd = new EventListenerMetaData(getAttr(attrs, "class"));
                if (md instanceof AbstractClassMetaData)
                {
                    // Specified at <entity> or <mapped-superclass>
                    ((AbstractClassMetaData)md).addListener(elmd);
                }
                else if (md instanceof FileMetaData)
                {
                    // Specified at <persistence-unit-defaults>
                    ((FileMetaData)md).addListener(elmd);
                }

                pushStack(elmd);
            }
            else if (localName.equals("pre-persist"))
            {
                // Pre-create callback
                MetaData md = getStack();
                if (md instanceof AbstractClassMetaData)
                {
                    // Specified at <entity> or <mapped-superclass>
                    AbstractClassMetaData cmd = (AbstractClassMetaData)md;
                    EventListenerMetaData elmd = cmd.getListenerForClass(cmd.getFullClassName());
                    if (elmd == null)
                    {
                        elmd = new EventListenerMetaData(cmd.getFullClassName());
                        cmd.addListener(elmd);
                    }
                    elmd.addCallback("javax.persistence.PrePersist", getAttr(attrs, "method-name"));
                }
                else
                {
                    // Specified at <entity-listener>
                    EventListenerMetaData elmd = (EventListenerMetaData)md;
                    elmd.addCallback("javax.persistence.PrePersist", getAttr(attrs, "method-name"));
                }
            }
            else if (localName.equals("post-persist"))
            {
                // Post-create callback
                MetaData md = getStack();
                if (md instanceof AbstractClassMetaData)
                {
                    // Specified at <entity> or <mapped-superclass>
                    AbstractClassMetaData cmd = (AbstractClassMetaData)md;
                    EventListenerMetaData elmd = cmd.getListenerForClass(cmd.getFullClassName());
                    if (elmd == null)
                    {
                        elmd = new EventListenerMetaData(cmd.getFullClassName());
                        cmd.addListener(elmd);
                    }
                    elmd.addCallback("javax.persistence.PostPersist", getAttr(attrs, "method-name"));
                }
                else
                {
                    // Specified at <entity-listener>
                    EventListenerMetaData elmd = (EventListenerMetaData)md;
                    elmd.addCallback("javax.persistence.PostPersist", getAttr(attrs, "method-name"));
                }
            }
            else if (localName.equals("pre-remove"))
            {
                // Pre-delete callback
                MetaData md = getStack();
                if (md instanceof AbstractClassMetaData)
                {
                    // Specified at <entity> or <mapped-superclass>
                    AbstractClassMetaData cmd = (AbstractClassMetaData)md;
                    EventListenerMetaData elmd = cmd.getListenerForClass(cmd.getFullClassName());
                    if (elmd == null)
                    {
                        elmd = new EventListenerMetaData(cmd.getFullClassName());
                        cmd.addListener(elmd);
                    }
                    elmd.addCallback("javax.persistence.PreRemove", getAttr(attrs, "method-name"));
                }
                else
                {
                    // Specified at <entity-listener>
                    EventListenerMetaData elmd = (EventListenerMetaData)md;
                    elmd.addCallback("javax.persistence.PreRemove", getAttr(attrs, "method-name"));
                }
            }
            else if (localName.equals("post-remove"))
            {
                // Post-delete callback
                MetaData md = getStack();
                if (md instanceof AbstractClassMetaData)
                {
                    // Specified at <entity> or <mapped-superclass>
                    AbstractClassMetaData cmd = (AbstractClassMetaData)md;
                    EventListenerMetaData elmd = cmd.getListenerForClass(cmd.getFullClassName());
                    if (elmd == null)
                    {
                        elmd = new EventListenerMetaData(cmd.getFullClassName());
                        cmd.addListener(elmd);
                    }
                    elmd.addCallback("javax.persistence.PostRemove", getAttr(attrs, "method-name"));
                }
                else
                {
                    // Specified at <entity-listener>
                    EventListenerMetaData elmd = (EventListenerMetaData)md;
                    elmd.addCallback("javax.persistence.PostRemove", getAttr(attrs, "method-name"));
                }
            }
            else if (localName.equals("pre-update"))
            {
                // Pre-store callback
                MetaData md = getStack();
                if (md instanceof AbstractClassMetaData)
                {
                    // Specified at <entity> or <mapped-superclass>
                    AbstractClassMetaData cmd = (AbstractClassMetaData)md;
                    EventListenerMetaData elmd = cmd.getListenerForClass(cmd.getFullClassName());
                    if (elmd == null)
                    {
                        elmd = new EventListenerMetaData(cmd.getFullClassName());
                        cmd.addListener(elmd);
                    }
                    elmd.addCallback("javax.persistence.PreUpdate", getAttr(attrs, "method-name"));
                }
                else
                {
                    // Specified at <entity-listener>
                    EventListenerMetaData elmd = (EventListenerMetaData)md;
                    elmd.addCallback("javax.persistence.PreUpdate", getAttr(attrs, "method-name"));
                }
            }
            else if (localName.equals("post-update"))
            {
                // Post-store callback
                MetaData md = getStack();
                if (md instanceof AbstractClassMetaData)
                {
                    // Specified at <entity> or <mapped-superclass>
                    AbstractClassMetaData cmd = (AbstractClassMetaData)md;
                    EventListenerMetaData elmd = cmd.getListenerForClass(cmd.getFullClassName());
                    if (elmd == null)
                    {
                        elmd = new EventListenerMetaData(cmd.getFullClassName());
                        cmd.addListener(elmd);
                    }
                    elmd.addCallback("javax.persistence.PostUpdate", getAttr(attrs, "method-name"));
                }
                else
                {
                    // Specified at <entity-listener>
                    EventListenerMetaData elmd = (EventListenerMetaData)md;
                    elmd.addCallback("javax.persistence.PostUpdate", getAttr(attrs, "method-name"));
                }
            }
            else if (localName.equals("post-load"))
            {
                // Post-load callback
                MetaData md = getStack();
                if (md instanceof AbstractClassMetaData)
                {
                    // Specified at <entity> or <mapped-superclass>
                    AbstractClassMetaData cmd = (AbstractClassMetaData)md;
                    EventListenerMetaData elmd = cmd.getListenerForClass(cmd.getFullClassName());
                    if (elmd == null)
                    {
                        elmd = new EventListenerMetaData(cmd.getFullClassName());
                        cmd.addListener(elmd);
                    }
                    elmd.addCallback("javax.persistence.PostLoad", getAttr(attrs, "method-name"));
                }
                else
                {
                    // Specified at <entity-listener>
                    EventListenerMetaData elmd = (EventListenerMetaData)md;
                    elmd.addCallback("javax.persistence.PostLoad", getAttr(attrs, "method-name"));
                }
            }
            else if (localName.equals("attribute-override"))
            {
                MetaData md = getStack();
                if (md instanceof AbstractClassMetaData)
                {
                    // Override columns for a superclass field
                    AbstractClassMetaData cmd = (AbstractClassMetaData)md;
                    AbstractMemberMetaData fmd = newOverriddenFieldObject(cmd, attrs);
                    cmd.addMember(fmd);
                    pushStack(fmd);
                }
                else
                {
                    // Override mappings for embedded field
                    AbstractMemberMetaData mmd = (AbstractMemberMetaData)md;
                    EmbeddedMetaData embmd = mmd.getEmbeddedMetaData();
                    if (embmd == null)
                    {
                        embmd = new EmbeddedMetaData();
                        embmd.setParent(mmd);
                        mmd.setEmbeddedMetaData(embmd);
                    }
                    AbstractMemberMetaData embMmd = newOverriddenEmbeddedFieldObject(embmd, attrs);
                    embmd.addMember(embMmd);
                    pushStack(mmd);
                }
            }
            else if (localName.equals("association-override"))
            {
                MetaData md = getStack();
                if (md instanceof AbstractClassMetaData)
                {
                    // Override columns for a superclass field
                    AbstractClassMetaData cmd = (AbstractClassMetaData)getStack();
                    AbstractMemberMetaData fmd = newOverriddenFieldObject(cmd, attrs);
                    cmd.addMember(fmd);
                    pushStack(fmd);
                }
                else
                {
                    // Override mappings for embedded field
                    AbstractMemberMetaData mmd = (AbstractMemberMetaData)md;
                    EmbeddedMetaData embmd = mmd.getEmbeddedMetaData();
                    if (embmd == null)
                    {
                        embmd = new EmbeddedMetaData();
                        embmd.setParent(mmd);
                        mmd.setEmbeddedMetaData(embmd);
                    }
                    AbstractMemberMetaData embMmd = newOverriddenEmbeddedFieldObject(embmd, attrs);
                    embmd.addMember(embMmd);
                    pushStack(mmd);
                }
            }
            else if (localName.equals("exclude-default-listeners"))
            {
                AbstractClassMetaData cmd = (AbstractClassMetaData)getStack();
                cmd.excludeDefaultListeners();
            }
            else if (localName.equals("exclude-superclass-listeners"))
            {
                AbstractClassMetaData cmd = (AbstractClassMetaData)getStack();
                cmd.excludeSuperClassListeners();
            }
            else
            {
                String message = LOCALISER.msg("044037", qName);
                NucleusLogger.METADATA.error(message);
                throw new RuntimeException(message);
            }
        }
        catch(RuntimeException ex)
        {
            NucleusLogger.METADATA.error(LOCALISER.msg("044042", qName, getStack(), uri), ex);
            throw ex;
        }
    }

    /**
     * Handler method called at the end of an element.
     * @param uri URI of the tag
     * @param localName local name
     * @param qName Name of element just ending
     * @throws SAXException in parsing errors
     */
    public void endElement(String uri, String localName, String qName)
    throws SAXException
    {
        if (localName.length()<1)
        {
            localName = qName;
        }
        // Save the current string for elements that have a body value
        String currentString = getString().trim();
        if (currentString.length() > 0)
        {
            MetaData md = getStack();
            if (localName.equals("schema"))
            {
                if (md instanceof FileMetaData)
                {
                    // Specified at <entity-mappings> or <persistence-unit-defaults>
                    ((FileMetaData)md).setSchema(currentString);
                }
            }
            else if (localName.equals("catalog"))
            {
                if (md instanceof FileMetaData)
                {
                    // Specified at <entity-mappings> or <persistence-unit-defaults>
                    ((FileMetaData)md).setCatalog(currentString);
                }
            }
            else if (localName.equals("access"))
            {
                if (md instanceof FileMetaData)
                {
                    // Specified at <entity-mappings> or <persistence-unit-defaults>
                    if (currentString.equalsIgnoreCase("PROPERTY"))
                    {
                        // Use property access
                        propertyAccess = true;
                    }
                }
            }
            else if (localName.equals("package"))
            {
                if (md instanceof FileMetaData)
                {
                    // Add the default package
                    FileMetaData filemd = (FileMetaData)md;
                    filemd.newPackageMetadata(currentString);
                    defaultPackageName = currentString;
                }
            }
            else if (localName.equals("discriminator-value"))
            {
                if (md instanceof ClassMetaData)
                {
                    // Add the discriminator value
                    ClassMetaData cmd = (ClassMetaData)md;
                    InheritanceMetaData inhmd = cmd.getInheritanceMetaData();
                    if (inhmd == null)
                    {
                        // Add an empty inheritance specification
                        inhmd = new InheritanceMetaData();
                        cmd.setInheritanceMetaData(inhmd);
                    }
                    String discrimValue = currentString;
                    DiscriminatorMetaData dismd = inhmd.getDiscriminatorMetaData();
                    if (dismd == null)
                    {
                        dismd = inhmd.newDiscriminatorMetadata();
                    }
                    dismd.setValue(discrimValue);
                    dismd.setStrategy(DiscriminatorStrategy.VALUE_MAP);
                }
            }
            else if (localName.equals("column-name"))
            {
                if (md instanceof UniqueMetaData)
                {
                    // Column for a unique constraint
                    ColumnMetaData colmd = new ColumnMetaData();
                    colmd.setName(currentString);
                    ((UniqueMetaData)md).addColumn(colmd);
                }
            }
            else if (localName.equals("order-by"))
            {
                if (md instanceof AbstractMemberMetaData)
                {
                    // "Ordered List" so add its ordering constraint
                    AbstractMemberMetaData fmd = (AbstractMemberMetaData)md;
                    OrderMetaData ordmd = new OrderMetaData();
                    ordmd.setOrdering(currentString);
                    fmd.setOrderMetaData(ordmd);
                }
            }
            else if (localName.equals("query"))
            {
                if (md instanceof QueryMetaData)
                {
                    // Named query, so set the query string
                    ((QueryMetaData)md).setQuery(currentString);
                }
            }
            else if (localName.equals("enumerated"))
            {
                if (md instanceof AbstractMemberMetaData)
                {
                    AbstractMemberMetaData mmd = (AbstractMemberMetaData)md;
                    String enumerationType = currentString;
                    String jdbcType = "INTEGER";
                    if (enumerationType.equalsIgnoreCase("STRING"))
                    {
                        jdbcType = "VARCHAR";
                    }
                    if (mmd.getColumnMetaData() == null)
                    {
                        ColumnMetaData colmd = new ColumnMetaData();
                        colmd.setJdbcType(jdbcType);
                        mmd.addColumn(colmd);
                    }
                    else
                    {
                        mmd.getColumnMetaData()[0].setJdbcType(jdbcType);
                    }
                }
            }
            else if (localName.equals("temporal"))
            {
                if (md instanceof AbstractMemberMetaData)
                {
                    AbstractMemberMetaData mmd = (AbstractMemberMetaData)md;
                    String enumerationType = currentString;
                    String jdbcType = null;
                    if (enumerationType.equalsIgnoreCase("DATE"))
                    {
                        jdbcType = "DATE";
                    }
                    else if (enumerationType.equalsIgnoreCase("TIME"))
                    {
                        jdbcType = "TIME";
                    }
                    else if (enumerationType.equalsIgnoreCase("TIMESTAMP"))
                    {
                        jdbcType = "TIMESTAMP";
                    }
                    if (mmd.getColumnMetaData() == null)
                    {
                        ColumnMetaData colmd = new ColumnMetaData();
                        colmd.setJdbcType(jdbcType);
                        mmd.addColumn(colmd);
                    }
                    else
                    {
                        mmd.getColumnMetaData()[0].setJdbcType(jdbcType);
                    }
                }
            }
        }

        if (localName.equals("attribute-override") || localName.equals("association-override"))
        {
            overrideMmd = null;
        }

        // Pop the tag
        // If startElement pushes an element onto the stack need a remove here for that type
        if (localName.equals("entity") ||
            localName.equals("mapped-superclass") ||
            localName.equals("embeddable") ||
            localName.equals("entity-listener") ||
            localName.equals("attribute-override") ||
            localName.equals("association-override") ||
            localName.equals("id") ||
            localName.equals("embedded-id") ||
            localName.equals("basic") ||
            localName.equals("transient") ||
            localName.equals("embedded") ||
            localName.equals("one-to-one") ||
            localName.equals("one-to-many") ||
            localName.equals("many-to-one") ||
            localName.equals("many-to-many") ||
            localName.equals("element-collection") ||
            localName.equals("version") ||
            localName.equals("secondary-table") ||
            localName.equals("join-table") ||
            localName.equals("unique-constraint") ||
            localName.equals("named-query") ||
            localName.equals("named-native-query") ||
            localName.equals("sql-result-set-mapping"))
        {
            popStack();
        }
    }
}
TOP

Related Classes of org.datanucleus.api.jpa.metadata.JPAMetaDataHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.