Package org.jpox.metadata

Source Code of org.jpox.metadata.InterfaceMetaData

/**********************************************************************
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:
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.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;

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

/**
* Representation of the MetaData of a "persistent-interface".
*
* @since 1.1
* @version $Revision: 1.44 $
*/
public class InterfaceMetaData extends AbstractClassMetaData
{
    // -------------------------------------------------------------------------
    // 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.
    /** 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 Type of identity
     * @param objectidClass Class of the object id
     * @param requiresExtent Whether the class requires an extent
     * @param detachable Whether this is detachable
     * @param embeddedOnly embedded-only tag
     * @param catalog Name for catalog
     * @param schema Name for schema
     * @param table Name of the table where to persist objects of this type
     * @param entityName the entity name required by JPA �4.3.1
     */
    public InterfaceMetaData(final PackageMetaData parent,
                             final String name,
                             final String identityType,
                             final String objectidClass,
                             final String requiresExtent,
                             final String detachable,
                             final String embeddedOnly,
                             final String catalog,
                             final String schema,
                             final String table,
                             final String entityName)
    {
        super(parent, name, identityType, objectidClass, requiresExtent, detachable, embeddedOnly, ClassPersistenceModifier.PERSISTENCE_CAPABLE.toString(),
            null, catalog, schema, table, entityName);
    }

    /**
     * Method to initialise the object, creating internal convenience arrays.
     * Initialises all sub-objects. If populate() is going to be used it should
     * be used BEFORE calling this method.
     */
    public 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 of the relevant category
        Iterator fields_iter = members.iterator();
        int no_of_managed_fields = 0;
        int no_of_overridden_fields = 0;
        while (fields_iter.hasNext())
        {
            AbstractMemberMetaData fmd = (AbstractMemberMetaData)fields_iter.next();

            // Initialise the AbstractMemberMetaData (and its sub-objects)
            fmd.initialise();
            if (fmd.isJdoField())
            {
                if (fmd.fieldBelongsToClass())
                {
                    no_of_managed_fields++;
                }
                else
                {
                    no_of_overridden_fields++;
                }
            }
        }

        // Generate the "managed fields" list
        managedMembers = new AbstractMemberMetaData[no_of_managed_fields];
        overriddenMembers = new AbstractMemberMetaData[no_of_overridden_fields];

        fields_iter = members.iterator();
        int field_id = 0;
        int overridden_field_id = 0;
        memberPositionsByName = new HashMap();
        while (fields_iter.hasNext())
        {
            AbstractMemberMetaData fmd = (AbstractMemberMetaData)fields_iter.next();

            if (fmd.isJdoField())
            {
                if (fmd.fieldBelongsToClass())
                {
                    fmd.setFieldId(field_id);
                    managedMembers[field_id] = fmd;
                    memberPositionsByName.put(fmd.getName(),new Integer(field_id));
                    field_id++;
                }
                else
                {
                    overriddenMembers[overridden_field_id++] = fmd;
                }
            }
        }

        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;
            }
        }

        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 (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;
        setInitialised();
    }

    /**
     * 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 AbstractMemberMetaData 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);

            // 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);
            }

            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
                populatePropertyMetaData(clr, cls, true, primary); // Make sure all PK fields (and superclasses) are set before objectid-class
                determineObjectIdClass(clr);
                populatePropertyMetaData(clr, cls, false, primary); // Populate non-PK fields
            }
            else
            {
                populatePropertyMetaData(clr, cls, true, primary);
                populatePropertyMetaData(clr, cls, false, primary);
                determineObjectIdClass(clr);
            }
           
            validateUserInputForInheritanceMetaData();
           
            determineInheritanceMetaData();

            validateDeprecatedMetaData();

            setPopulated();
        }
        catch (RuntimeException e)
        {
            JPOXLogger.METADATA.debug(e);
            throw e;
        }
        finally
        {
            populating = false;
        }
    }
  
    /**
     * Utility to add a defaulted PropertyMetaData to the class.
     * Provided as a method since then any derived classes can override it.
     * @param name name of field
     * @return the new PropertyMetaData
     */
    protected AbstractMemberMetaData newDefaultedProperty(String name)
    {
        return new PropertyMetaData(this, name);
    }

    /**
     * Populate PropertyMetaData.
     * @param clr The ClassLoader
     * @param cls This class
     * @param pkFields Process pk fields (or non-PK fields 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 populatePropertyMetaData(ClassLoaderResolver clr, Class cls, boolean pkFields, ClassLoader primary)
    {
        Collections.sort(members);

        // Populate the AbstractMemberMetaData 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 (pkFields == fmd.isPrimaryKey())
            {
                Class fieldCls = cls;
                if (!fmd.fieldBelongsToClass())
                {
                    // Field overrides a field in a superclass, so find the class
                    try
                    {
                        fieldCls = clr.classForName(fmd.getClassName(), primary);
                    }
                    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, primary);
                            fmd.setClassName(fieldClassName);
                        }
                        catch (ClassNotResolvedException cnre2)
                        {
                            JPOXLogger.METADATA.error(LOCALISER.msg("044080", fieldClassName));
                            throw new InvalidMetaDataException(LOCALISER, "044080", fieldClassName);
                        }
                    }
                }

                Method cls_method = null;
                try
                {
                    try
                    {
                        cls_method = fieldCls.getDeclaredMethod(ClassUtils.getJavaBeanGetterName(fmd.getName(),true),null);
                    }
                    catch(Exception e)
                    {
                        cls_method = fieldCls.getDeclaredMethod(ClassUtils.getJavaBeanGetterName(fmd.getName(),false),null);
                    }
                }
                catch (Exception e)
                {
                    // MetaData method doesn't exist in the class!
                    throw new InvalidMetaDataException(LOCALISER,
                        "044072", fullName, fmd.getFullFieldName());
                }
                fmd.populate(clr, null, cls_method, primary);
            }
        }
    }

    /**
     * Add MetaData for properties of the interface not declared in MetaData.
     * @param cls Class represented by this metadata
     */
    protected void addMetaDataForMembersNotInMetaData(Class cls)
    {
        // Add MetaData for properties for the interface that aren't in the XML/annotations.
        // 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 properties in superclasses
        // NOTE 2 : We ignore "enhanced" properties (added by the enhancer)
        // NOTE 3 : We ignore inner class fields (containing "$")
        // NOTE 4 : We sort the properties into ascending alphabetical order
        Collections.sort(members);
        try
        {
            // Get 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 inner class methods, and that aren't static
                if (clsMethods[i].getDeclaringClass().getName().equals(fullName) &&
                    (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 fieldName = ClassUtils.getFieldNameForJavaBeanGetter(clsMethods[i].getName());
                    if (Collections.binarySearch(members, fieldName) < 0) // PropertyMetaData implements Comparable
                    {
                        // Check if a setter is also present
                        String setterName = ClassUtils.getJavaBeanSetterName(fieldName);
                        for (int j=0;j<clsMethods.length;j++)
                        {
                            if (clsMethods[j].getName().equals(setterName))
                            {
                                // Getter/Setter for a property but not in MetaData so add
                                JPOXLogger.METADATA.debug(LOCALISER.msg("044060",
                                    fieldName, name));
                                AbstractMemberMetaData mmd = newDefaultedProperty(fieldName);
                                members.add(mmd);
                                Collections.sort(members);
                                break;
                            }
                        }
                    }
                }
            }
        }
        catch (Exception e)
        {
            JPOXLogger.METADATA.error(e.getMessage(), e);
            throw new JPOXUserException(e.getMessage());
        }
    }

    // ------------------------------ 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("<interface 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=\"false\"");
        }
        if (embeddedOnly)
        {
            sb.append(prefix).append("       embedded-only=\"true\"\n");
        }
        if (detachable)
        {
            sb.append(prefix).append("       detachable=\"true\"\n");
        }
        if (table != null)
        {
            sb.append(prefix).append("       table=\"" + table + "\"\n");
        }
        sb.append(">\n");

        // 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));
        }

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

        // Add joins
        if (joins != null)
        {
            for (int i=0; i<joins.size(); i++)
            {
                JoinMetaData jmd = (JoinMetaData)joins.get(i);
                sb.append(jmd.toString(prefix + indent, indent));
            }
        }

        // Add foreign-keys
        if (foreignKeys != null)
        {
            for (int i=0; i<foreignKeys.size(); i++)
            {
                ForeignKeyMetaData fkmd = (ForeignKeyMetaData)foreignKeys.get(i);
                sb.append(fkmd.toString(prefix + indent, indent));
            }
        }
       
        // Add indexes
        if (indexes != null)
        {
            for (int i=0; i<indexes.size(); i++)
            {
                IndexMetaData imd = (IndexMetaData)indexes.get(i);
                sb.append(imd.toString(prefix + indent, indent));
            }
        }
       
        // Add unique constraints
        if (uniqueConstraints != null)
        {
            for (int i=0; i<uniqueConstraints.size(); i++)
            {
                UniqueMetaData unimd = (UniqueMetaData)uniqueConstraints.get(i);
                sb.append(unimd.toString(prefix + indent, indent));
            }
        }
       
        // Add properties
        if (members != null)
        {
            for (int i=0;i<members.size();i++)
            {
                PropertyMetaData pmd = (PropertyMetaData)members.get(i);
                sb.append(pmd.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 (fetchGroups != null)
        {
            for (int i=0; i<fetchGroups.size(); i++)
            {
                FetchGroupMetaData fgmd = (FetchGroupMetaData)fetchGroups.get(i);
                sb.append(fgmd.toString(prefix + indent, indent));
            }
        }

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

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

Related Classes of org.jpox.metadata.InterfaceMetaData

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.