Package org.datanucleus.metadata

Source Code of org.datanucleus.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.datanucleus.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.datanucleus.ClassLoaderResolver;
import org.datanucleus.exceptions.ClassNotResolvedException;
import org.datanucleus.exceptions.NucleusException;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.util.ClassUtils;
import org.datanucleus.util.NucleusLogger;

/**
* Representation of the MetaData of a "persistent-interface".
*/
public class InterfaceMetaData extends AbstractClassMetaData
{
    /**
     * 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
     */
    public InterfaceMetaData(final PackageMetaData parent, final String name)
    {
        super(parent, name);
    }

    /**
     * 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.
     * @param clr ClassLoader resolver
     * @param mmgr MetaData manager
     */
    public synchronized void initialise(ClassLoaderResolver clr, MetaDataManager mmgr)
    {
        if (initialising || isInitialised())
        {
            return;
        }

        checkPopulated();

        try
        {
            initialising = true;

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

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

            // Validate the objectid-class
            // This must be in initialise() since can be dependent on other classes being populated
            validateObjectIdClass(clr, mmgr);

            // 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(clr, mmgr);
                if (fmd.isFieldToBePersisted())
                {
                    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.isFieldToBePersisted())
                {
                    if (fmd.fieldBelongsToClass())
                    {
                        fmd.setFieldId(field_id);
                        managedMembers[field_id] = fmd;
                        memberPositionsByName.put(fmd.getName(), Integer.valueOf(field_id));
                        field_id++;
                    }
                    else
                    {
                        overriddenMembers[overridden_field_id++] = fmd;
                    }
                }
            }

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

            // Set up the various convenience arrays of field numbers
            initialiseMemberPositionInformation(mmgr);

            joinMetaData = new JoinMetaData[joins.size()];
            for (int i=0; i<joinMetaData.length; i++)
            {
                joinMetaData[i] = joins.get(i);
                joinMetaData[i].initialise(clr, mmgr);
            }
            indexMetaData = new IndexMetaData[indexes.size()];
            for (int i=0; i<indexMetaData.length; i++)
            {
                indexMetaData[i] = indexes.get(i);
                indexMetaData[i].initialise(clr, mmgr);
            }
            foreignKeyMetaData = new ForeignKeyMetaData[foreignKeys.size()];
            for (int i=0; i<foreignKeyMetaData.length; i++)
            {
                foreignKeyMetaData[i] = foreignKeys.get(i);
                foreignKeyMetaData[i].initialise(clr, mmgr);
            }
            uniqueMetaData = new UniqueMetaData[uniqueConstraints.size()];
            for (int i=0; i<uniqueMetaData.length; i++)
            {
                uniqueMetaData[i] = uniqueConstraints.get(i);
                uniqueMetaData[i].initialise(clr, mmgr);
            }

            fetchGroupMetaData = new FetchGroupMetaData[fetchGroups.size()];
            fetchGroupMetaDataByName = new HashMap();
            for (int i=0; i<fetchGroupMetaData.length; i++)
            {
                fetchGroupMetaData[i] = fetchGroups.get(i);
                fetchGroupMetaData[i].initialise(clr, mmgr);
                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();
                    identityMetaData.setColumnName(superImd.getColumnName());
                    identityMetaData.setValueStrategy(superImd.getValueStrategy());
                    identityMetaData.setSequence(superImd.getSequence());
                }
                else
                {
                    identityMetaData = new IdentityMetaData();
                }
            }

            if (versionMetaData != null)
            {
                versionMetaData.initialise(clr, mmgr);
            }
            if (identityMetaData != null)
            {
                identityMetaData.initialise(clr, mmgr);
            }
            if (inheritanceMetaData != null)
            {
                inheritanceMetaData.initialise(clr, mmgr);
            }

            if (identityType == IdentityType.APPLICATION)
            {
                usesSingleFieldIdentityClass = mmgr.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();
        }
        finally
        {
            initialising = false;

            mmgr.abstractClassMetaDataInitialised(this);
        }
    }

    /**
     * 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 specified 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)
     * @param mmgr MetaData manager
     */
    public synchronized void populate(ClassLoaderResolver clr, ClassLoader primary, MetaDataManager mmgr)
    {
        if (isInitialised() || isPopulated())
        {
            NucleusLogger.METADATA.error(LOCALISER.msg("044068",name));
            throw new NucleusException(LOCALISER.msg("044068",fullName)).setFatal();
        }
        if (populating)
        {
            return;
        }

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

            Class cls = loadClass(clr, primary, mmgr);

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

            // Load any ORM definition for this class
            mmgr.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, mmgr);

            inheritIdentity();
            determineIdentity();
            validateUserInputForIdentity();

            addMetaDataForMembersNotInMetaData(cls);

            // Set inheritance
            validateUserInputForInheritanceMetaData();
            determineInheritanceMetaData(mmgr);
            applyDefaultDiscriminatorValueWhenNotSpecified();

            if (objectidClass == null)
            {
                // No user-defined objectid-class but potentially have SingleFieldIdentity so make sure PK fields are set
                populatePropertyMetaData(clr, cls, true, primary, mmgr); // PK fields
                determineObjectIdClass(mmgr);
                populatePropertyMetaData(clr, cls, false, primary, mmgr); // Non-PK fields
            }
            else
            {
                populatePropertyMetaData(clr, cls, true, primary, mmgr);
                populatePropertyMetaData(clr, cls, false, primary, mmgr);
                determineObjectIdClass(mmgr);
            }

            setPopulated();
        }
        catch (RuntimeException e)
        {
            NucleusLogger.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)
     * @param mmgr MetaData manager
     * @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, MetaDataManager mmgr)
    {
        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)
                        {
                            NucleusLogger.METADATA.error(LOCALISER.msg("044080", fieldClassName));
                            throw new InvalidMetaDataException(LOCALISER, "044080", fieldClassName);
                        }
                    }
                }

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

    /**
     * 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 memberName = ClassUtils.getFieldNameForJavaBeanGetter(clsMethods[i].getName());
                    // TODO : This will not check if the name is a property! so we can miss field+property clashes
                    if (Collections.binarySearch(members, memberName) < 0)
                    {
                        // Check if a setter is also present
                        String setterName = ClassUtils.getJavaBeanSetterName(memberName);
                        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
                                NucleusLogger.METADATA.debug(LOCALISER.msg("044060", memberName, name));
                                AbstractMemberMetaData mmd = newDefaultedProperty(memberName);
                                members.add(mmd);
                                Collections.sort(members);
                                break;
                            }
                        }
                    }
                }
            }
        }
        catch (Exception e)
        {
            NucleusLogger.METADATA.error(e.getMessage(), e);
            throw new NucleusUserException(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 = 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 = 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 = 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 = 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 = 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.datanucleus.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.