Package org.jpox.metadata

Source Code of org.jpox.metadata.ClassMetaData

/**********************************************************************
Copyright (c) 2004 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:
2004 Kikuchi Kousuke - org.jpox.enhancer.conf.JDOConfigClass
2004 Marco Schulze (NightLabs) - changed the behaviour to warn only if
        an inherited class declares an own objectid and it equals the
        one of the superclass.
2004 Andy Jefferson - Added discriminator/inheritance checks
2004 Erik Bengtson - changes for application identity
2004 Andy Jefferson - moved PK class checks out into JDOUtils
2007 Xuan Baldauf - little reduction in code duplication to anticipate changes regarding issue http://www.jpox.org/servlet/jira/browse/CORE-3272
    ...
**********************************************************************/
package org.jpox.metadata;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import org.jpox.ClassLoaderResolver;
import org.jpox.exceptions.ClassNotResolvedException;
import org.jpox.exceptions.JPOXException;
import org.jpox.util.ClassUtils;
import org.jpox.util.JPOXLogger;

/**
* Representation of the MetaData of a class. Extends the abstract definition to include
* implementations, fields, embedded-only tags. Has a parent PackageMetaData that
* can contain the metadata for several classes.
*
* <H3>Lifecycle state</H3>
* This object supports 3 lifecycle states. The first is the raw
* constructed object which represents pure MetaData (maybe from a MetaData
* file). The second is a "populated" object which represents MetaData for a
* Class with the metadata aligned to be appropriate for that Class.
* The third is "initialised" once the internal arrays are created.
* This object, once populated, will represent ALL fields in the class
* (including static, final and transient fields).
*
* <H3>Fields/Properties</H3>
* This object keeps a list of FieldMetaData/PropertyMetaData objects for the fields of this class.
* In addition it has an array of FieldMetaData
* objects representing those that are actually managed by JDO
* ("managedFields"). This second set does not contain things like static, final
* or transient fields since JDO doesn't support those yet.
* <P>Fields are of 2 types. The first are normal fields of this class.
* These have their own "relative" field number, relative to this class.
* The second type are "overriding" fields which override the baseline field
* in a superclass. These fields have no "relative" field number since they are
* relative to this class (and such a relative field number would make no sense).
* Fields are all added through addField() during the parse process, and
* are updated during the populate/initialise process to define their relative field
* numbers. Please refer to FieldMetaData for more details of fields.
*
* <H3>Numbering of fields</H3>
* Fields of the class are numbered in 2 ways. The first way is the numbering
* within a class. In a class, the field 'id's will start at 0. If a class is
* inherited, it will also have a second numbering for its fields - the
* "absolute" numbering. With "absolute" numbering, the fields start at the
* first field in the root superclass which has absolute number 0, and they are
* numbered from there, navigating down the hierarchy. In terms of what is
* stored in the records, the FieldMetaData stores fieldId as the first
* method (relative to the class it is in). The "absolute" numbering is
* always derived from this and the inheritance hierarchy.
*
* <H3>MetaData Element</H3>
* The MetaData Element represented here is as follows
* <PRE>
* &lt;!ELEMENT class (datastore-identity?, implements*, inheritance?, version?, join*,
*      foreign-key*, index*, unique*, field*, column*, query*, fetch-group*, extension*)&gt;
* &lt;!ATTLIST class name CDATA #REQUIRED&gt;
* &lt;!ATTLIST class identity-type (application|datastore|nondurable) #IMPLIED&gt;
* &lt;!ATTLIST class catalog CDATA #IMPLIED&gt;
* &lt;!ATTLIST class schema CDATA #IMPLIED&gt;
* &lt;!ATTLIST class table CDATA #IMPLIED&gt;
* &lt;!ATTLIST class persistence-capable-superclass CDATA #IMPLIED&gt;
* &lt;!ATTLIST class objectid-class CDATA #IMPLIED&gt;
* &lt;!ATTLIST class requires-extent (true|false) 'true'&gt;
* &lt;!ATTLIST class detachable (true|false) 'true'&gt;
* &lt;!ATTLIST class embedded-only (true|false) #IMPLIED&gt;
* &lt;!ATTLIST class persistence-modifier
*      (persistence-capable|persistence-aware|non-persistent) #IMPLIED&gt;
* </PRE>
*
* @since 1.1
* @version $Revision: 1.136 $
*/
public class ClassMetaData extends AbstractClassMetaData
{
    /** List of implements. */
    protected List implementations = new ArrayList();

    // -------------------------------------------------------------------------
    // Fields below here are not represented in the output MetaData. They are
    // for use internally in the operation of the JDO system. The majority are
    // for convenience to save iterating through the fields since the fields
    // are fixed once initialised.

    /*** ImplementsMetaData */
    protected ImplementsMetaData[] implementsMetaData;

    /** is the persistable class abstract. */
    protected boolean isAbstractPersistenceCapable;

    /** whether the populate method is running **/
    private boolean populating = false;

    // ----------------------------- Constructors ------------------------------
    /**
     * Constructor.
     * Takes the basic string information found in the MetaData file.
     * @param parent The package to which this class belongs
     * @param name Name of class
     * @param identityType identity-type flag
     * @param objectidClass Primary key class name
     * @param requiresExtent Whether the class requires an extent
     * @param detachable Whether the class can be detached
     * @param modifier persistence-modifier tag
     * @param embeddedOnly embedded-only tag
     * @param persistenceCapableSuperclass Name of PC superclass
     * @param catalog Name for catalog
     * @param schema Name for schema
     * @param table RDBMS table to store the class in
     * @param entityName the entity name required by JPA �4.3.1
     */
    public ClassMetaData(final PackageMetaData parent,
                         final String name,
                         final String identityType,
                         final String objectidClass,
                         final String requiresExtent,
                         final String detachable,
                         final String embeddedOnly,
                         final String modifier,
                         final String persistenceCapableSuperclass,
                         final String catalog,
                         final String schema,
                         final String table,
                         final String entityName)
    {
        super(parent, name, identityType, objectidClass, requiresExtent, detachable, embeddedOnly, modifier, persistenceCapableSuperclass,
            catalog, schema, table,entityName);
    }

    /**
     * Constructor for creating the ClassMetaData for an implementation of a "persistent-interface".
     * @param imd MetaData for the "persistent-interface"
     * @param implClassName Name of the implementation class
     * @param copyFields Whether to copy the fields of the interface too
     */
    public ClassMetaData(final InterfaceMetaData imd, String implClassName, boolean copyFields)
    {
        super(imd, implClassName, copyFields);
    }

    /**
     * Constructor for creating the ClassMetaData for an implementation of a "persistent-abstract-class".
     * @param cmd MetaData for the implementation of the "persistent-abstract-class"
     * @param implClassName Name of the implementation class
     */
    public ClassMetaData(final ClassMetaData cmd, String implClassName)
    {
        super(cmd, implClassName);
    }

    /**
     * Method to provide the details of the class being represented by this
     * MetaData. This can be used to firstly provide defaults for attributes
     * that aren't specified in the MetaData, and secondly to report any errors
     * with attributes that have been specifed that are inconsistent with the
     * class being represented.
     * <P>
     * One possible use of this method would be to take a basic ClassMetaData
     * for a class and call this, passing in the users class. This would then
     * add FieldMetaData for all fields in this class providing defaults for
     * all of these.
     *
     * @param clr ClassLoaderResolver to use in loading any classes
     * @param primary the primary ClassLoader to use (or null)
     */
    public synchronized void populate(ClassLoaderResolver clr, ClassLoader primary)
    {
        if (isInitialised() || isPopulated())
        {
            JPOXLogger.METADATA.error(LOCALISER.msg("044068",name));
            throw new JPOXException(LOCALISER.msg("044068",fullName)).setFatal();
        }
        if (populating)
        {
            return;
        }

        try
        {
            if (JPOXLogger.METADATA.isDebugEnabled())
            {
                JPOXLogger.METADATA.debug(LOCALISER.msg("044075",fullName));
            }
            populating = true;

            Class cls = loadClass(clr,primary);

            isAbstractPersistenceCapable = Modifier.isAbstract(cls.getModifiers());

            // Load any Annotations definition for this class
            if (!isMetaDataComplete())
            {
                getMetaDataManager().addAnnotationsDataToClass(cls, this, clr);
            }

            // Load any ORM definition for this class
            getMetaDataManager().addORMDataToClass(cls, clr);

            // If a class is an inner class and is non-static it is invalid
            if (ClassUtils.isInnerClass(fullName) && !Modifier.isStatic(cls.getModifiers()) &&
                persistenceModifier == ClassPersistenceModifier.PERSISTENCE_CAPABLE)
            {
                throw new InvalidMetaDataException(LOCALISER, "044063", fullName);
            }

            if (entityName == null)
            {
                // No entity name given so just default to the name of the class (without package)
                this.entityName = name;
            }

            determineSuperClassName(clr, cls);

            inheritDetachableSettings();
           
            inheritIdentity();
           
            determineIdentity();
           
            validateUserInputForIdentity();
           
            addMetaDataForMembersNotInMetaData(cls);

            if (objectidClass == null)
            {
                // No user-defined objectid-class but potentially have SingleFieldIdentity so make sure PK fields are set
                populateMemberMetaData(clr, cls, true, primary); // Make sure all PK fields (and superclasses) are set before objectid-class
                determineObjectIdClass(clr);
                populateMemberMetaData(clr, cls, false, primary); // Populate non-PK fields
            }
            else
            {
                populateMemberMetaData(clr, cls, true, primary);
                populateMemberMetaData(clr, cls, false, primary);
                determineObjectIdClass(clr);
            }

            validateUserInputForInheritanceMetaData();
           
            determineInheritanceMetaData();

            validateDeprecatedMetaData();

            validateUnmappedColumns();

            // populate the implements
            for(int i=0; i<implementations.size(); i++)
            {
                ((ImplementsMetaData)implementations.get(i)).populate(clr);
            }

            if (persistentInterfaceImplNeedingTableFromSuperclass)
            {
                // Need to go up to next superinterface and make sure its metadata is populated
                // until we find the next interface with metadata with inheritance strategy of "new-table".
                AbstractClassMetaData acmd = getMetaDataForSuperinterfaceManagingTable(cls, clr);
                if (acmd != null)
                {
                    table = acmd.table;
                    schema = acmd.schema;
                    catalog = acmd.catalog;
                }
                persistentInterfaceImplNeedingTableFromSuperclass = false;
            }
            else if (persistentInterfaceImplNeedingTableFromSubclass)
            {
                // TODO Cater for finding the subclass-table that manages our table
                persistentInterfaceImplNeedingTableFromSubclass = false;
            }

            setPopulated();
        }
        catch (RuntimeException e)
        {
            JPOXLogger.METADATA.debug(e);
            throw e;
        }
        finally
        {
            populating = false;
        }
    }

    /**
     * Method to find a superinterface with MetaData that specifies NEW_TABLE inheritance strategy
     * @param cls The class
     * @param clr ClassLoader resolver
     * @return The AbstractClassMetaData for the class managing the table
     */
    private AbstractClassMetaData getMetaDataForSuperinterfaceManagingTable(Class cls, ClassLoaderResolver clr)
    {
        Collection superinterfaces = ClassUtils.getSuperinterfaces(cls);
        Iterator iter = superinterfaces.iterator();
        while (iter.hasNext())
        {
            Class superintf = (Class)iter.next();
            AbstractClassMetaData acmd = getMetaDataManager().getMetaDataForInterface(superintf, clr);
            if (acmd != null && acmd.getInheritanceMetaData() != null)
            {
                if (acmd.getInheritanceMetaData().getStrategyValue() == InheritanceStrategy.NEW_TABLE)
                {
                    // Found it
                    return acmd;
                }
                else if (acmd.getInheritanceMetaData().getStrategyValue() == InheritanceStrategy.SUPERCLASS_TABLE)
                {
                    // Try further up the hierarchy
                    return getMetaDataForSuperinterfaceManagingTable(superintf, clr);
                }
            }
        }
        return null;
    }

    /**
     * Add MetaData of fields/properties not declared in MetaData.
     * @param cls Class represented by this metadata
     */
    protected void addMetaDataForMembersNotInMetaData(Class cls)
    {
        // Access API since we treat things differently for JPA and JDO
        String api = getMetaDataManager().getOMFContext().getApi();

        // Add fields/properties for the class that don't have MetaData.
        // We use Reflection here since JDOImplHelper would only give use info
        // for enhanced files (and the enhancer needs unenhanced as well).
        // NOTE 1 : We ignore fields/properties in superclasses
        // NOTE 2 : We ignore "enhanced" fields/properties (added by the JPOX enhancer)
        // NOTE 3 : We ignore inner class fields/properties (containing "$")
        // NOTE 4 : We sort the fields/properties into ascending alphabetical order
        Collections.sort(members);
        try
        {
            // check if we have any persistent properties (JPA)
            boolean hasProperties = false;
            for (int i=0; i<members.size(); i++)
            {
                if (((AbstractMemberMetaData)members.get(i)).isProperty())
                {
                    hasProperties = true;
                    break;
                }
            }

            if (hasProperties && api.equalsIgnoreCase("JPA"))
            {
                // JPA : when we are using properties go through and add properties for those not specified.
                // Process all (reflected) methods in the populating class
                Method[] clsMethods = cls.getDeclaredMethods();
                for (int i=0;i<clsMethods.length;i++)
                {
                    // Limit to getter methods in this class, that aren't enhancer-added methods
                    // that aren't inner class methods, and that aren't static
                    if (clsMethods[i].getDeclaringClass().getName().equals(fullName) &&
                        !clsMethods[i].getName().startsWith("jdo") &&
                        (clsMethods[i].getName().startsWith("get") || clsMethods[i].getName().startsWith("is")) &&
                        !ClassUtils.isInnerClass(clsMethods[i].getName()) &&
                        !Modifier.isStatic(clsMethods[i].getModifiers()))
                    {
                        // Find if there is metadata for this property
                        String propertyName = ClassUtils.getFieldNameForJavaBeanGetter(clsMethods[i].getName());
                        if (Collections.binarySearch(members, propertyName) < 0) // AbstractMemberMetaData implements Comparable
                        {
                            // No field/property of this name - add a default PropertyMetaData for this method
                            JPOXLogger.METADATA.debug(LOCALISER.msg("044060",
                                propertyName, name));
                            AbstractMemberMetaData fmd = getMetaDataManager().getMetaDataFactory().newPropertyObject(this,
                                propertyName, null, null, null, null, null, null, null, null,
                                null, null, null, null, null, null, null, null, null, null, null, null, null);
                            members.add(fmd);
                            Collections.sort(members);
                        }
                        else
                        {
                            // Field/property exists
                        }
                    }
                }
            }

            // Process all (reflected) fields in the populating class
            Field[] clsFields = cls.getDeclaredFields();
            for (int i=0;i<clsFields.length;i++)
            {
                // Limit to fields in this class, that aren't enhancer-added fields
                // that aren't inner class fields, and that aren't static
                if (clsFields[i].getDeclaringClass().getName().equals(fullName) &&
                    !clsFields[i].getName().startsWith("jdo") &&
                    !ClassUtils.isInnerClass(clsFields[i].getName()) &&
                    !Modifier.isStatic(clsFields[i].getModifiers()))
                {
                    // Find if there is metadata for this field.
                    // This is possible as FieldMetaData implements Comparable
                    if (Collections.binarySearch(members, clsFields[i].getName()) < 0)
                    {
                        // No field/property of this name
                        if (hasProperties && api.equalsIgnoreCase("JPA"))
                        {
                            // JPA : Class has properties but field not present, so add as transient field (JPA)
                            AbstractMemberMetaData fmd = getMetaDataManager().getMetaDataFactory().newFieldObject(this,
                                clsFields[i].getName(), null, "none", null, null, null, null, null, null,
                                null, null, null, null, null, null, null, null, null, null, null, null);
                            members.add(fmd);
                            Collections.sort(members);
                        }
                        else
                        {
                            // Class has fields but field not present, so add as field
                            JPOXLogger.METADATA.debug(LOCALISER.msg("044060",
                                clsFields[i].getName(), name));
                            AbstractMemberMetaData fmd = getMetaDataManager().getMetaDataFactory().newFieldObject(this,
                                clsFields[i].getName(), null, null, null, null, null, null, null, null,
                                null, null, null, null, null, null, null, null, null, null, null, null);
                            members.add(fmd);
                            Collections.sort(members);
                        }
                    }
                }
            }
        }
        catch (Exception e)
        {
            JPOXLogger.METADATA.error(e.getMessage(), e);
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * Populate MetaData for all members.
     * @param clr The ClassLoaderResolver
     * @param cls This class
     * @param pkMembers Process pk fields/properties (or non-PK if false)
     * @param primary the primary ClassLoader to use (or null)
     * @throws InvalidMetaDataException if the Class for a declared type in a field cannot be loaded by the <code>clr</code>
     * @throws InvalidMetaDataException if a field declared in the MetaData does not exist in the Class
     */
    protected void populateMemberMetaData(ClassLoaderResolver clr, Class cls, boolean pkMembers, ClassLoader primary)
    {
        Collections.sort(members);
       
        // Populate the FieldMetaData with their real field values
        // This will populate any containers in these fields also
        Iterator fields_iter=members.iterator();
        while (fields_iter.hasNext())
        {
            AbstractMemberMetaData fmd=(AbstractMemberMetaData)fields_iter.next();
            if (pkMembers == fmd.isPrimaryKey())
            {
                Class fieldCls = cls;
                if (fmd.className != null && fmd.className.equals("#UNKNOWN"))
                {
                    // Field is for a superclass but we didnt know which at creation so resolve it
                    if (pcSuperclassMetaData != null)
                    {
                        AbstractMemberMetaData superFmd = pcSuperclassMetaData.getMetaDataForMember(fmd.getName());
                        if (superFmd != null)
                        {
                            // Field is for a superclass so set its "className"
                            if (superFmd.className != null)
                            {
                                fmd.className = superFmd.className;
                            }
                            else
                            {
                                fmd.className = superFmd.getClassName();
                            }
                        }
                    }
                    else
                    {
                        // No superclass so it doenst make sense so assume to be for this class
                        fmd.className = null;
                    }
                }
                if (!fmd.fieldBelongsToClass())
                {
                    // Field overrides a field in a superclass, so find the class
                    try
                    {
                        fieldCls = clr.classForName(fmd.getClassName());
                    }
                    catch (ClassNotResolvedException cnre)
                    {
                        // Not found at specified location, so try the same package as this class
                        String fieldClassName = getPackageName() + "." + fmd.getClassName();
                        try
                        {
                            fieldCls = clr.classForName(fieldClassName);
                            fmd.setClassName(fieldClassName);
                        }
                        catch (ClassNotResolvedException cnre2)
                        {
                            JPOXLogger.METADATA.error(LOCALISER.msg("044080", fieldClassName));
                            throw new InvalidMetaDataException(LOCALISER, "044080", fieldClassName);
                        }
                    }
                }

                boolean populated = false;
                if (fmd.isProperty())
                {
                    // User class must have a getter and setter for this property as per Java Beans
                    Method getMethod = null;
                    try
                    {
                        // Find the getter
                        // a). Try as a standard form of getter (getXXX)
                        getMethod = fieldCls.getDeclaredMethod(
                            ClassUtils.getJavaBeanGetterName(fmd.getName(), false), null);
                    }
                    catch (Exception e)
                    {
                        try
                        {
                            // b). Try as a boolean form of getter (isXXX)
                            getMethod = fieldCls.getDeclaredMethod(
                                ClassUtils.getJavaBeanGetterName(fmd.getName(), true), null);
                        }
                        catch (Exception e2)
                        {
                        }
                    }
                    if (getMethod == null && fmd.getPersistenceModifier() != FieldPersistenceModifier.NONE)
                    {
                        // Property is persistent yet no getter!
                        throw new InvalidMetaDataException(LOCALISER,
                            "044073", fullName, fmd.getName());
                    }

                    Method setMethod = null;
                    try
                    {
                        // Find the setter
                        String setterName = ClassUtils.getJavaBeanSetterName(fmd.getName());
                        Method[] methods = fieldCls.getDeclaredMethods();
                        for (int i=0;i<methods.length;i++)
                        {
                            if (methods[i].getName().equals(setterName) && methods[i].getParameterTypes() != null &&
                                methods[i].getParameterTypes().length == 1)
                            {
                                setMethod = methods[i];
                            }
                        }
                    }
                    catch (Exception e)
                    {
                    }
                    if (setMethod == null && fmd.getPersistenceModifier() != FieldPersistenceModifier.NONE)
                    {
                        // Property is persistent yet no setter!
                        throw new InvalidMetaDataException(LOCALISER,
                            "044074", fullName, fmd.getName());
                    }

                    // Populate the property using the getter
                    if (getMethod != null)
                    {
                        fmd.populate(clr, null, getMethod, primary);
                        populated = true;
                    }
                }
                // TODO Why is this next block capable of processing things declared as property?
                if (!populated)
                {
                    Field cls_field = null;
                    try
                    {
                        cls_field = fieldCls.getDeclaredField(fmd.getName());
                    }
                    catch (Exception e)
                    {
                    }
                    if (cls_field != null)
                    {
                        fmd.populate(clr, cls_field, null, primary);
                        populated = true;
                    }
                }
                if (!populated)
                {
                    // MetaData field doesn't exist in the class!
                    throw new InvalidMetaDataException(LOCALISER,
                        "044071", fullName,fmd.getFullFieldName());
                }
            }
        }
    }
   
    /**
     * Method to initialise the object, creating internal convenience arrays.
     * Initialises all sub-objects. populate() should be called BEFORE calling this.
     */
    public synchronized void initialise()
    {
        if (populating)
        {
            return;
        }
        checkPopulated();
        if (isInitialised())
        {
            return;
        }

        if (pcSuperclassMetaData != null)
        {
            // We need our superclass to be initialised before us because we rely on information there
            if (!pcSuperclassMetaData.isInitialised())
            {
                pcSuperclassMetaData.initialise();
            }
        }

        if (JPOXLogger.METADATA.isDebugEnabled())
        {
            JPOXLogger.METADATA.debug(LOCALISER.msg("044076",fullName));
        }

        // Count the fields/properties of the relevant category
        Iterator membersIter = members.iterator();
        int numManaged = 0;
        int numOverridden = 0;
        while (membersIter.hasNext())
        {
            AbstractMemberMetaData fmd = (AbstractMemberMetaData)membersIter.next();

            // Initialise the FieldMetaData (and its sub-objects)
            fmd.initialise();
            if (fmd.isJdoField())
            {
                if (fmd.fieldBelongsToClass())
                {
                    numManaged++;
                }
                else
                {
                    numOverridden++;
                }
            }
        }

        // Generate the "managed members" list
        managedMembers = new AbstractMemberMetaData[numManaged];
        overriddenMembers = new AbstractMemberMetaData[numOverridden];

        membersIter = members.iterator();
        int field_id = 0;
        int overridden_field_id = 0;
        memberPositionsByName = new HashMap();
        while (membersIter.hasNext())
        {
            AbstractMemberMetaData mmd = (AbstractMemberMetaData)membersIter.next();
            if (mmd.isJdoField())
            {
                if (mmd.fieldBelongsToClass())
                {
                    mmd.setFieldId(field_id);
                    managedMembers[field_id] = mmd;
                    memberPositionsByName.put(mmd.getName(),new Integer(field_id));
                    field_id++;
                }
                else
                {
                    overriddenMembers[overridden_field_id++] = mmd;
                    AbstractMemberMetaData superFmd = pcSuperclassMetaData.getMemberBeingOverridden(mmd.getName());
                    if (superFmd != null)
                    {
                        // Merge in any additional info not specified in the overridden field
                        if (superFmd.isPrimaryKey())
                        {
                            mmd.setPrimaryKey();
                        }
                        // TODO Add on any more info not present
                    }
                }
            }
        }

        if (pcSuperclassMetaData != null)
        {
            if (!pcSuperclassMetaData.isInitialised())
            {
                pcSuperclassMetaData.initialise();
            }
            noOfInheritedManagedMembers = pcSuperclassMetaData.getNoOfInheritedManagedMembers() + pcSuperclassMetaData.getNoOfManagedMembers();
        }

        // Set up the various convenience arrays of field numbers
        int total_field_count=noOfInheritedManagedMembers + managedMembers.length;
        this.memberCount = total_field_count;
        allMemberPositions = new int[total_field_count];
        dfgMemberFlags = new boolean[total_field_count];
        scoMutableMemberFlags = new boolean[total_field_count];
        nonPkMemberFlags = new boolean[total_field_count];
        int pk_field_count=0;
        int dfg_field_count=0;
        int scm_field_count=0;
        int pc_field_count=0;
        for (int i=0;i<total_field_count;i++)
        {
            allMemberPositions[i] = i;

            AbstractMemberMetaData fmd=getMetaDataForManagedMemberAtAbsolutePositionInternal(i);
            if (fmd.isPrimaryKey())
            {
                pk_field_count++;
            }
            else
            {
                nonPkMemberFlags[i] = true;
            }
            if (fmd.isDefaultFetchGroup())
            {
                dfgMemberFlags[i] = true;
                dfg_field_count++;
            }
            if (fmd.calcIsSecondClassMutable())
            {
                scoMutableMemberFlags[i] = true;
                scm_field_count++;
            }
            if (fmd.isFieldTypePersistable())
            {
                pc_field_count++;
            }
        }
        if (pk_field_count > 0 &&
            identityType != IdentityType.APPLICATION)
        {
            // primary key fields found, but not using application identity
            throw new InvalidMetaDataException(LOCALISER,
                "044078",
                fullName,new Integer(pk_field_count),identityType);
        }
        else if (pk_field_count > 0)
        {
            pkMemberPositions = new int[pk_field_count];
            for (int i=0,pk_num=0;i<total_field_count;i++)
            {
                AbstractMemberMetaData fmd=getMetaDataForManagedMemberAtAbsolutePositionInternal(i);
                if (fmd.isPrimaryKey())
                {
                    pkMemberPositions[pk_num++] = i;
                }
            }
        }
        else if (pk_field_count == 0 &&
            identityType == IdentityType.APPLICATION)
        {
            // No primary key fields found
            throw new InvalidMetaDataException(LOCALISER,
                "044077",
                fullName,objectidClass);
        }

        nonPkMemberPositions = new int[total_field_count-pk_field_count];
        persistenceCapableMemberPositions = new int[pc_field_count];
        for (int i=0,npkf=0,npc=0;i<total_field_count;i++)
        {
            AbstractMemberMetaData fmd = getMetaDataForManagedMemberAtAbsolutePositionInternal(i);
            if (!fmd.isPrimaryKey())
            {
                nonPkMemberPositions[npkf++] = i;
            }
            if (fmd.isFieldTypePersistable())
            {
                persistenceCapableMemberPositions[npc++] = i;
            }
        }

        dfgMemberPositions = new int[dfg_field_count];
        scoMutableMemberPositions = new int[scm_field_count];
        for (int i=0,dfg_num=0,scm_num=0;i<total_field_count;i++)
        {
            if (dfgMemberFlags[i])
            {
                dfgMemberPositions[dfg_num++] = i;
            }
            if (scoMutableMemberFlags[i])
            {
                scoMutableMemberPositions[scm_num++] = i;
            }
        }

        // Initialise any sub-objects
        implementsMetaData = new ImplementsMetaData[implementations.size()];
        for (int i=0; i<implementations.size(); i++)
        {
            implementsMetaData[i] = (ImplementsMetaData) implementations.get(i);
            implementsMetaData[i].initialise();
        }
        joinMetaData = new JoinMetaData[joins.size()];
        for (int i=0; i<joinMetaData.length; i++)
        {
            joinMetaData[i] = (JoinMetaData) joins.get(i);
            joinMetaData[i].initialise();
        }
        indexMetaData = new IndexMetaData[indexes.size()];
        for (int i=0; i<indexMetaData.length; i++)
        {
            indexMetaData[i] = (IndexMetaData) indexes.get(i);
            indexMetaData[i].initialise();
        }
        foreignKeyMetaData = new ForeignKeyMetaData[foreignKeys.size()];
        for (int i=0; i<foreignKeyMetaData.length; i++)
        {
            foreignKeyMetaData[i] = (ForeignKeyMetaData) foreignKeys.get(i);
            foreignKeyMetaData[i].initialise();
        }
        uniqueMetaData = new UniqueMetaData[uniqueConstraints.size()];
        for (int i=0; i<uniqueMetaData.length; i++)
        {
            uniqueMetaData[i] = (UniqueMetaData) uniqueConstraints.get(i);
            uniqueMetaData[i].initialise();
        }

        fetchGroupMetaData = new FetchGroupMetaData[fetchGroups.size()];
        fetchGroupMetaDataByName = new HashMap();
        for (int i=0; i<fetchGroupMetaData.length; i++)
        {
            fetchGroupMetaData[i] = (FetchGroupMetaData) fetchGroups.get(i);
            fetchGroupMetaData[i].initialise();
            fetchGroupMetaDataByName.put(fetchGroupMetaData[i].getName(), fetchGroupMetaData[i]);
        }        
       
        // If using datastore id and user hasn't provided the identity element,
        // add a defaulted one (using the superclass if available)
        if (identityType == IdentityType.DATASTORE && identityMetaData == null)
        {
            if (pcSuperclassMetaData != null)
            {
                IdentityMetaData superImd = pcSuperclassMetaData.getIdentityMetaData();
                identityMetaData = new IdentityMetaData(this, superImd.getColumnName(), superImd.getValueStrategy().toString(), superImd.getSequence());
            }
            else
            {
                identityMetaData = new IdentityMetaData(this,null,null,null);
            }
        }

        if (primaryKeyMetaData != null)
        {
            primaryKeyMetaData.initialise();
        }
        if (versionMetaData != null)
        {
            versionMetaData.initialise();
        }
        if (identityMetaData != null)
        {
            identityMetaData.initialise();
        }
        if (inheritanceMetaData != null)
        {
            inheritanceMetaData.initialise();
        }

        if (identityType == IdentityType.APPLICATION)
        {
            usesSingleFieldIdentityClass = getMetaDataManager().getApiAdapter().isSingleFieldIdentityClass(getObjectidClass());
        }

        // Clear out the collections that we used when loading the MetaData
        joins.clear();
        joins = null;
        fetchGroups.clear();
        fetchGroups = null;
        foreignKeys.clear();
        foreignKeys = null;
        indexes.clear();
        indexes = null;
        uniqueConstraints.clear();
        uniqueConstraints = null;
        implementations.clear();
        implementations = null;
        setInitialised();
    }

    /**
     * Utility to add a defaulted FieldMetaData to the class. Provided as a
     * method since then any derived classes can override it (e.g
     * JDOConfigClass can create a JDOConfigField)
     * @param name name of field
     * @return the new FieldMetaData
     */
    protected AbstractMemberMetaData newDefaultedProperty(String name)
    {
        return new FieldMetaData(this, name);
    }

    // ------------------------------ Accessors --------------------------------

    /**
     * Accessor for the implements MetaData
     * @return Returns the implements MetaData.
     */
    public final ImplementsMetaData[] getImplementsMetaData()
    {
        return implementsMetaData;
    }

    /**
     * Convenience accessor for whether this class implements a specified interface
     * @param interfaceName The name of the interface
     * @return Whether it implements the interface
     */
    public boolean implementsInterface(String interfaceName)
    {
        if (implementsMetaData != null)
        {
            for (int i=0;i<implementsMetaData.length;i++)
            {
                if (implementsMetaData[i].getName().equals(interfaceName))
                {
                    return true;
                }
            }
        }
        return false;
    }


    /**
     * Whether the PersistenceCapable class is abstract
     * @return true if the PersistenceCapable class is abstract
     */
    public boolean isAbstractPersistenceCapable()
    {
        return isAbstractPersistenceCapable;
    }  
   
    // ------------------------------- Mutators --------------------------------

    /**
     * Method to add an implements to this class.
     * @param implmd Meta-Data for the implements
     */
    public void addImplements(ImplementsMetaData implmd)
    {
        if (implmd == null)
        {
            return;
        }

        if (isInitialised())
        {
            throw new RuntimeException("Already initialised");
        }
        implementations.add(implmd);
    }

    // ------------------------------ Utilities --------------------------------

    /**
     * Returns a string representation of the object.
     * This can be used as part of a facility to output a MetaData file.
     * @param prefix prefix string
     * @param indent indent string
     * @return a string representation of the object.
     */
    public String toString(String prefix,String indent)
    {
        StringBuffer sb = new StringBuffer();
        sb.append(prefix).append("<class name=\"" + name + "\"\n");
        if (identityType != null)
        {
            sb.append(prefix).append("       identity-type=\"" + identityType + "\"\n");
        }
        if (objectidClass != null)
        {
            sb.append(prefix).append("       objectid-class=\"" + objectidClass + "\"\n");
        }
        if (!requiresExtent)
        {
            sb.append(prefix).append("       requires-extent=\"" + requiresExtent + "\"");
        }
        if (embeddedOnly)
        {
            sb.append(prefix).append("       embedded-only=\"" + embeddedOnly + "\"\n");
        }
        if (persistenceModifier != null)
        {
            sb.append(prefix).append("       persistence-modifier=\"" + persistenceModifier + "\"\n");
        }
        if (catalog != null)
        {
            sb.append(prefix).append("       catalog=\"" + catalog + "\"\n");
        }
        if (schema != null)
        {
            sb.append(prefix).append("       schema=\"" + schema + "\"\n");
        }
        if (table != null)
        {
            sb.append(prefix).append("       table=\"" + table + "\"\n");
        }
        if (detachable)
        {
            sb.append(prefix).append("       detachable=\"" + detachable + "\">\n");
        }

        // Implements
        if (implementsMetaData != null)
        {
            for (int i=0; i<implementsMetaData.length; i++)
            {
                sb.append(implementsMetaData[i].toString(prefix + indent, indent));
            }
        }

        // Identity
        if (identityMetaData != null)
        {
            sb.append(identityMetaData.toString(prefix + indent,indent));
        }

        // PrimaryKey
        if (primaryKeyMetaData != null)
        {
            sb.append(primaryKeyMetaData.toString(prefix + indent,indent));
        }

        // Inheritance
        if (inheritanceMetaData != null)
        {
            sb.append(inheritanceMetaData.toString(prefix + indent,indent));
        }

        // Add Version
        if (versionMetaData != null)
        {
            sb.append(versionMetaData.toString(prefix + indent,indent));
        }

        // Add joins
        if (joinMetaData != null)
        {
            for (int i=0; i<joinMetaData.length; i++)
            {
                sb.append(joinMetaData[i].toString(prefix + indent,indent));
            }
        }

        // Add foreign-keys
        if (foreignKeyMetaData != null)
        {
            for (int i=0; i<foreignKeyMetaData.length; i++)
            {
                sb.append(foreignKeyMetaData[i].toString(prefix + indent,indent));
            }
        }

        // Add indexes
        if (indexMetaData != null)
        {
            for (int i=0; i<indexMetaData.length; i++)
            {
                sb.append(indexMetaData[i].toString(prefix + indent,indent));
            }
        }

        // Add unique constraints
        if (uniqueMetaData != null)
        {
            for (int i=0; i<uniqueMetaData.length; i++)
            {
                sb.append(uniqueMetaData[i].toString(prefix + indent,indent));
            }
        }

        // Add fields
        if (managedMembers != null)
        {
            for (int i=0; i<managedMembers.length; i++)
            {
                sb.append(managedMembers[i].toString(prefix + indent,indent));
            }
        }

        // Add unmapped columns
        if (unmappedColumns != null)
        {
            for (int i=0;i<unmappedColumns.size();i++)
            {
                ColumnMetaData col = (ColumnMetaData)unmappedColumns.get(i);
                sb.append(col.toString(prefix + indent, indent));
            }
        }

        // Add queries
        if (queries != null)
        {
            Iterator iter = queries.iterator();
            while (iter.hasNext())
            {
                QueryMetaData q = (QueryMetaData)iter.next();
                sb.append(q.toString(prefix + indent,indent));
            }
        }

        // Add fetch-groups
        if (fetchGroupMetaData != null)
        {
            for (int i=0; i<fetchGroupMetaData.length; i++)
            {
                sb.append(fetchGroupMetaData[i].toString(prefix + indent,indent));
            }
        }

        // Add extensions
        sb.append(super.toString(prefix + indent,indent));

        sb.append(prefix + "</class>\n");
        return sb.toString();
    }
}
TOP

Related Classes of org.jpox.metadata.ClassMetaData

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.