Package org.jpox.metadata

Source Code of org.jpox.metadata.MetaDataManager$InterfaceClassComparator

/**********************************************************************
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:
    ...
**********************************************************************/
package org.jpox.metadata;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.jdo.spi.JDOImplHelper;
import javax.jdo.spi.RegisterClassEvent;
import javax.jdo.spi.RegisterClassListener;

import org.jpox.ClassLoaderResolver;
import org.jpox.OMFContext;
import org.jpox.ObjectManagerFactoryImpl;
import org.jpox.PersistenceConfiguration;
import org.jpox.api.ApiAdapter;
import org.jpox.exceptions.ClassNotResolvedException;
import org.jpox.exceptions.JPOXException;
import org.jpox.exceptions.JPOXUserException;
import org.jpox.metadata.annotations.AnnotationManager;
import org.jpox.metadata.xml.MetaDataParser;
import org.jpox.util.ClassUtils;
import org.jpox.util.JPOXLogger;
import org.jpox.util.JavaUtils;
import org.jpox.util.Localiser;
import org.jpox.util.StringUtils;

/**
* Manager of MetaData information in JPOX having scope of an ObjectManagerFactory.
* Each PMF/EMF will effectively have a single MetaDataManager handling all XML/Annotations MetaData.
* <p>
* A MetaDataManager can be "initialised" to start with particular MetaData. This is typically performed
* when the operation has a "persistence-unit", which defines the classes/jars/mapping-files to use.
* Alternatively, it can be initialised using a set of classes and mapping-files (for use by SchemaTool
* and the Enhancer when specifying the files as input).
* <P>
* Acts as a registry of metadata so that metadata files don't need to be
* parsed multiple times. MetaData is stored as a FileMetaData, which contains
* PackageMetaData, which contains ClassMetaData, and so on. This maps exactly
* to the users model of their metadata. The users access point is
* <B>getMetaDataForClass()</B> which will check the known classes without metadata,
* then check the existing registered metdata, then check the valid locations for
* metdata files. This way, the metadata is managed from this single point.
* </P>
* <P>
* Maintains a list of all classes that have been checked for MetaData and
* don't have any available. This avoids the needs to look up MetaData multiple
* times finding the same result. Currently this list is for all ClassMetaData
* objects keyed by the class name.
* </P>
* TODO Update MetaDataManager to be able to share ClassMetaData with other MetaDataManager
* so we need a central (singleton/static) registry that allows such communication.
*
* @version $Revision: 1.170 $
*/
public abstract class MetaDataManager
{
    /** Localiser for messages. */
    protected static final Localiser LOCALISER = Localiser.getInstance("org.jpox.metadata.Localisation",
        ObjectManagerFactoryImpl.class.getClassLoader());

    /** The ObjectManagerFactory Context that this metadata manager is operating in. */
    protected OMFContext omfContext = null;

    /** Manager for annotations. */
    protected AnnotationManager annotationManager = null;

    /** Parser for MetaData. */
    protected MetaDataParser metaDataParser = null;

    /** Flag whether we should validate the metadata files when parsing. */
    protected boolean validateMetaData = true;

    /** Cache of class names that are known to not have MetaData/annotations. */
    protected Collection classesWithoutPersistenceInfo = new HashSet();

    /** Map of FileMetaData for the parsed files, keyed by the URL string. */
    protected Map fileMetaDataByURLString = new HashMap();

    /** Map of ClassMetaData, keyed by the class name. */
    protected Map classMetaDataByClass = new HashMap();

    /** Map of ClassMetaData, keyed by the JPA "entity name". */
    protected Map classMetaDataByEntityName = new HashMap();

    /** Map<String, Set<String>>: Cache subclass information as that is expensive to compute, keyed by class name */
    protected Map directSubclassesByClass = new HashMap();

    /** Map of QueryMetaData, keyed by the (class name + query name). */
    protected Map queryMetaDataByName = new HashMap();

    /** Map of FetchPlanMetaData, keyed by the fetch plan name. */
    protected Map fetchPlanMetaDataByName = new HashMap();

    /** Map of SequenceMetaData, keyed by the package name and sequence name. */
    protected Map sequenceMetaDataByPackageSequence = new HashMap();

    /** Map of TableGeneratorMetaData, keyed by the package name and generator name. */
    protected Map tableGeneratorMetaDataByPackageSequence = new HashMap();

    /** Map of QueryResultMetaData keyed by the name. */
    protected Map queryResultMetaDataByName = new HashMap();

    /** Indicator for whether this manager is managing the enhancement process, else it's runtime. */
    protected boolean enhancing = false;

    /** Flag for initialisation state when starting using a "persistence-unit". Used to prevent double initialise. */
    protected boolean initialised = false;

    /** EventListeners. Use a list to preserve ordering. */
    protected List listeners = new ArrayList();

    /** Factory for MetaData objects (class, field, property, interface). */
    private MetaDataFactory metaDataFactory;

    /**
     * Whether the MetaData manager supports ORM concepts and metadata.
     * When using an object datastore this will be false.
     */
    protected boolean supportsORM = true;

    /**
     * Constructor, specifying the OMFContext used.
     * @param ctxt OMF Context that this metadata manager operates in
     */
    public MetaDataManager(OMFContext ctxt)
    {
        this.omfContext = ctxt;
        PersistenceConfiguration conf = omfContext.getPersistenceConfiguration();
        JDOImplHelper.getInstance().removeRegisterClassListener(new MetaDataRegisterClassListener());
        this.setValidate(conf.getBooleanProperty("org.jpox.metadata.validate"));

        if (JavaUtils.isJRE1_5OrAbove())
        {
            try
            {
                ClassLoaderResolver clr = ctxt.getClassLoaderResolver(null);
                Class annotationReaderClass = clr.classForName(conf.getStringProperty("org.jpox.metadata.annotationManager"));

                Class[] ctrArgs = new Class[] {MetaDataManager.class};
                Object[] ctrParams = new Object[] {this};
                Constructor ctor = annotationReaderClass.getConstructor(ctrArgs);
                annotationManager = (AnnotationManager)ctor.newInstance(ctrParams);
            }
            catch (Exception e)
            {
                JPOXLogger.METADATA.info(LOCALISER.msg("044045"));
            }
        }

        // Register all of the types managed by the TypeManager as not needing MetaData/Annotations.
        Set supportedClasses = getOMFContext().getTypeManager().getSupportedTypes();
        Iterator iter = supportedClasses.iterator();
        while (iter.hasNext())
        {
            classesWithoutPersistenceInfo.add(iter.next());
        }

        if (omfContext.getStoreManager() != null)
        {
            // Object datastores dont "map" for persistence so dont need ORM
            supportsORM = omfContext.getStoreManager().getSupportedOptions().contains("ORM");
        }
    }

    /**
     * Accessor for whether the MetaData manager supports ORM concepts and metadata.
     * With object datastores this will return false.
     * @return Whether we support ORM
     */
    public boolean supportsORM()
    {
        return supportsORM;
    }

    /**
     * Method to log the configuration of this manager.
     */
    protected void logConfiguration()
    {
        // Log the configuration
        String inputTypes = null;
        if (annotationManager != null)
        {
            inputTypes = "XML, Annotations";
        }
        else
        {
            inputTypes = "XML";
        }

        JPOXLogger.METADATA.debug("MetaDataManager : " +
            " Input=(" + inputTypes + ")" +
            " XML-Validation=" + validateMetaData);
    }

    /**
     * Initialisation method to define the initial metadata files and class names being handled by this MetaDataManager.
     * @param metadataFiles The metadata files
     * @param classNames The class names
     * @param clr ClassLoader Resolver
     * @return Array of the FileMetaData that is managed
     * @throws JPOXUserException (with nested exceptions) if an error occurs parsing the files
     */
    public FileMetaData[] initialise(String[] metadataFiles, String[] classNames, ClassLoaderResolver clr)
    {
        if (initialised)
        {
            // TODO Throw exception
            return null;
        }
        if (JPOXLogger.METADATA.isDebugEnabled())
        {
            JPOXLogger.METADATA.debug(LOCALISER.msg("044013"));
        }

        HashSet exceptions = new HashSet();
        ArrayList fileMetaData = new ArrayList();

        // Load MetaData files first
        for (int i=0; i<metadataFiles.length; i++)
        {
            try
            {
                URL fileURL = null;
                try
                {
                    fileURL = new URL("file:" + metadataFiles[i]);
                }
                catch (MalformedURLException mue)
                {
                    fileURL = clr.getResource(metadataFiles[i], null);
                }
                if (fileURL != null && fileMetaDataByURLString.get(fileURL.toString()) == null)
                {
                    FileMetaData filemd = parseFile(fileURL);
                    if (filemd != null)
                    {
                        registerFile(fileURL.toString(), filemd, clr);
                        fileMetaData.add(filemd);
                    }
                    else
                    {
                        throw new JPOXUserException(LOCALISER.msg("044015",
                            metadataFiles[i]));
                    }
                }
            }
            catch (Exception e)
            {
                exceptions.add(e);
            }
        }

        // Load classes
        for (int i=0;i<classNames.length;i++)
        {
            try
            {
                Class cls = clr.classForName(classNames[i]);
                // Check for MetaData for this class (take precedence over annotations if they exist)
                AbstractClassMetaData cmd = (AbstractClassMetaData)classMetaDataByClass.get(classNames[i]);
                if (cmd == null)
                {
                    // No MetaData so try annotations
                    FileMetaData filemd = loadAnnotationsForClass(cls, clr, true, false);
                    if (filemd != null)
                    {
                        // Store file against an annotations specific "URL"
                        registerFile("annotations:" + classNames[i], filemd, clr);
                        fileMetaData.add(filemd);
                    }
                    else
                    {
                        // Class has no metadata or annotations so warn the user
                        JPOXLogger.METADATA.info(LOCALISER.msg("044017",
                            classNames[i]));
                    }
                }
                else
                {
                    // We have MetaData, and any annotations will be merged in during the populate process
                }
            }
            catch (ClassNotResolvedException e)
            {
                // log and ignore this exception
                JPOXLogger.METADATA.error(StringUtils.getStringFromStackTrace(e));
            }
            catch (Exception e)
            {
                exceptions.add(e);
            }
        }

        if (exceptions.size() > 0)
        {
            // Exceptions while loading MetaData/annotations
            throw new JPOXUserException(LOCALISER.msg("044016"),
                (Throwable[]) exceptions.toArray(new Throwable[exceptions.size()]),null);
        }

        if (fileMetaData.size() > 0)
        {
            // MetaData has been loaded for the unit
            // a). Populate MetaData
            if (JPOXLogger.METADATA.isDebugEnabled())
            {
                JPOXLogger.METADATA.debug(LOCALISER.msg("044018"));
            }
            Iterator iter = fileMetaData.iterator();
            while (iter.hasNext())
            {
                FileMetaData filemd = (FileMetaData)iter.next();
                populateFileMetaData(filemd, clr, null);
            }

            // b). Initialise MetaData
            if (JPOXLogger.METADATA.isDebugEnabled())
            {
                JPOXLogger.METADATA.debug(LOCALISER.msg("044019"));
            }
            iter = fileMetaData.iterator();
            while (iter.hasNext())
            {
                FileMetaData filemd = (FileMetaData)iter.next();
                try
                {
                    initialiseFileMetaData(filemd, clr, null);
                }
                catch (Exception e)
                {
                    exceptions.add(e);
                }
            }
            if (exceptions.size() > 0)
            {
                throw new JPOXUserException(LOCALISER.msg("044020"),
                    (Throwable[])exceptions.toArray(new Throwable[exceptions.size()]));
            }
        }

        if (JPOXLogger.METADATA.isDebugEnabled())
        {
            JPOXLogger.METADATA.debug(LOCALISER.msg("044014"));
        }
        initialised = true;
        return (FileMetaData[])fileMetaData.toArray(new FileMetaData[fileMetaData.size()]);
    }

    /**
     * Initialisation method to define the "persistence-unit" being handled by this MetaDataManager.
     * @param persistenceUnitName name of the "persistence-unit"
     * @param clr ClassLoader Resolver
     * @return Array of the FileMetaData that is managed
     * @throws JPOXUserException if an error occurs parsing the persistence-unit info
     */
    public FileMetaData[] initialise(String persistenceUnitName, ClassLoaderResolver clr)
    {
        PersistenceUnitMetaData pumd = null;
        pumd = getMetaDataForPersistenceUnit(persistenceUnitName);
        if (pumd == null)
        {
            throw new JPOXUserException(LOCALISER.msg("044047", persistenceUnitName));
        }
        else
        {
            return initialise(pumd, clr);
        }
    }

    /**
     * Initialisation method to define the "persistence-unit" being handled by this MetaDataManager.
     * @param pumd The MetaData for this "persistence-unit"
     * @param clr ClassLoader Resolver
     * @return Array of the FileMetaData that is managed
     * @throws JPOXUserException if an error occurs parsing the persistence-unit info
     */
    public FileMetaData[] initialise(PersistenceUnitMetaData pumd, ClassLoaderResolver clr)
    {
        if (initialised)
        {
            // TODO Throw exception
            return null;
        }
        if (JPOXLogger.METADATA.isDebugEnabled())
        {
            JPOXLogger.METADATA.debug(LOCALISER.msg("044021", pumd.getName()));
        }

        HashSet exceptions = new HashSet();
        ArrayList fileMetaData = new ArrayList();

        HashSet mappingFiles = new HashSet();
        Set classNames = new HashSet();


        // Generate list of mapping files
        mappingFiles.add("META-INF/orm.xml"); // Default location
        if (pumd.getMappingFiles() != null)
        {
            // <mapping-file>
            mappingFiles.addAll(pumd.getMappingFiles());
        }
        if (omfContext.getApi().equalsIgnoreCase("JDO")) // When in JDO mode grab any package.jdo
        {
            // <jar-file>
            HashSet jarFileNames = pumd.getJarFiles();
            if (jarFileNames != null)
            {
                Iterator iter = jarFileNames.iterator();
                while (iter.hasNext())
                {
                    Object jarFile = iter.next();
                    if (jarFile instanceof String)
                    {
                        String[] packageJdoFiles = ClassUtils.getPackageJdoFilesForJarFile((String)jarFile);
                        if (packageJdoFiles != null)
                        {
                            for (int i=0;i<packageJdoFiles.length;i++)
                            {
                                mappingFiles.add(packageJdoFiles[i]);
                            }
                        }
                    }
                    else if (jarFile instanceof URL)
                    {
                        String[] packageJdoFiles = ClassUtils.getPackageJdoFilesForJarFile((URL)jarFile);
                        if (packageJdoFiles != null)
                        {
                            for (int i=0;i<packageJdoFiles.length;i++)
                            {
                                mappingFiles.add(packageJdoFiles[i]);
                            }
                        }
                    }
                }
            }
        }

        // Generate list of class names
        if (pumd.getClassNames() != null)
        {
            classNames.addAll(pumd.getClassNames());
        }
        if (annotationManager != null && !enhancing) // TODO Why not when enhancing? document it
        {
            HashSet jarFileNames = pumd.getJarFiles();
            if (jarFileNames != null)
            {
                Iterator iter = jarFileNames.iterator();
                while (iter.hasNext())
                {
                    Object jarFile = iter.next();
                    if (jarFile instanceof String)
                    {
                        String[] jarClassNames = ClassUtils.getClassNamesForJarFile((String)jarFile);
                        if (jarClassNames != null)
                        {
                            for (int i=0;i<jarClassNames.length;i++)
                            {
                                classNames.add(jarClassNames[i]);
                            }
                        }
                    }
                    else if (jarFile instanceof URL)
                    {
                        String[] jarClassNames = ClassUtils.getClassNamesForJarFile((URL)jarFile);
                        if (jarClassNames != null)
                        {
                            for (int i=0;i<jarClassNames.length;i++)
                            {
                                classNames.add(jarClassNames[i]);
                            }
                        }
                    }
                }
            }
        }

        // Load XML metadata for all <mapping-file> specifications
        if (mappingFiles != null && mappingFiles.size() > 0)
        {
            Iterator iter = mappingFiles.iterator();
            while (iter.hasNext())
            {
                String mappingFileName = (String)iter.next();
                try
                {
                    Enumeration files = clr.getResources(mappingFileName, Thread.currentThread().getContextClassLoader());
                    while (files.hasMoreElements())
                    {
                        URL url = (URL)files.nextElement();
                        if (url != null && fileMetaDataByURLString.get(url.toString()) == null)
                        {
                            FileMetaData filemd = parseFile(url);
                            if (filemd != null)
                            {
                                // Register the file
                                registerFile(url.toString(), filemd, clr);
                                fileMetaData.add(filemd);
                            }
                        }
                    }
                }
                catch (InvalidMetaDataException imde)
                {
                    // Error in the metadata for this file
                    JPOXLogger.METADATA.error(StringUtils.getStringFromStackTrace(imde));
                    exceptions.add(imde);
                }
                catch (IOException ioe)
                {
                    JPOXLogger.METADATA.error(LOCALISER.msg("044027",
                        pumd.getName(), mappingFileName, ioe.getMessage()), ioe);
                }
            }
        }

        // Load annotation metadata for all classes
        if (classNames.size() > 0 && annotationManager != null)
        {
            // Process all classes
            Iterator iter = classNames.iterator();
            while (iter.hasNext())
            {
                // Check for MetaData for this class (take precedence over annotations if they exist)
                String className = (String)iter.next();
                AbstractClassMetaData cmd = (AbstractClassMetaData)classMetaDataByClass.get(className);
                if (cmd == null)
                {
                    // No MetaData so try annotations
                    try
                    {
                        Class cls = clr.classForName(className);
                        FileMetaData filemd = loadAnnotationsForClass(cls, clr, true, false);
                        if (filemd != null)
                        {
                            fileMetaData.add(filemd);
                        }
                    }
                    catch (Exception e)
                    {
                        exceptions.add(e);
                    }
                }
                else
                {
                    // We have MetaData, and any annotations will be merged in during the populate process
                }
            }
        }

        if (exceptions.size() > 0)
        {
            throw new JPOXUserException(LOCALISER.msg("044026",
                pumd.getName()), (Throwable[])exceptions.toArray(new Throwable[exceptions.size()]));
        }

        if (fileMetaData.size() > 0)
        {
            // MetaData has been loaded for the unit
            // a). Populate MetaData
            if (JPOXLogger.METADATA.isDebugEnabled())
            {
                JPOXLogger.METADATA.debug(LOCALISER.msg("044024",
                    pumd.getName()));
            }
            Iterator iter = fileMetaData.iterator();
            while (iter.hasNext())
            {
                FileMetaData filemd = (FileMetaData)iter.next();
                populateFileMetaData(filemd, clr, null);
            }

            // b). Initialise MetaData
            if (JPOXLogger.METADATA.isDebugEnabled())
            {
                JPOXLogger.METADATA.debug(LOCALISER.msg("044025",
                    pumd.getName()));
            }
            iter = fileMetaData.iterator();
            while (iter.hasNext())
            {
                FileMetaData filemd = (FileMetaData)iter.next();
                try
                {
                    initialiseFileMetaData(filemd, clr, null);
                }
                catch (Exception e)
                {
                    JPOXLogger.METADATA.error(StringUtils.getStringFromStackTrace(e));
                    exceptions.add(e);
                }
            }
            if (exceptions.size() > 0)
            {
                throw new JPOXUserException(LOCALISER.msg("044026",
                    pumd.getName()), (Throwable[])exceptions.toArray(new Throwable[exceptions.size()]));
            }
        }

        if (JPOXLogger.METADATA.isDebugEnabled())
        {
            JPOXLogger.METADATA.debug(LOCALISER.msg("044022", pumd.getName()));
        }
        initialised = true;
        return (FileMetaData[])fileMetaData.toArray(new FileMetaData[fileMetaData.size()]);
    }

    /**
     * Mutator for whether to validate the MetaData files for XML compliance.
     * @param validate Whether to validate
     */
    public void setValidate(boolean validate)
    {
        validateMetaData = validate;
    }

    /**
     * Accessor for the ObjectManagerFactory Context that this manager is running in.
     * @return The ObjectManagerFactory Context
     */
    public OMFContext getOMFContext()
    {
        return omfContext;
    }

    /**
     * Accessor for the API adapter being used by this MetaDataManager.
     * @return API adapter.
     */
    public ApiAdapter getApiAdapter()
    {
        return omfContext.getApiAdapter();
    }

    /**
     * Clear resources
     */
    public void close()
    {
        classMetaDataByClass.clear();
        queryMetaDataByName.clear();
        fetchPlanMetaDataByName.clear();
        sequenceMetaDataByPackageSequence.clear();
        tableGeneratorMetaDataByPackageSequence.clear();
        queryResultMetaDataByName.clear();
        fileMetaDataByURLString.clear();
        classesWithoutPersistenceInfo.clear();
        classMetaDataByClass = null;
        queryMetaDataByName = null;
        fetchPlanMetaDataByName = null;
        sequenceMetaDataByPackageSequence = null;
        tableGeneratorMetaDataByPackageSequence = null;
        fileMetaDataByURLString = null;
        classesWithoutPersistenceInfo = null;
    }

    /**
     * Accessor for whether we are managing the enhancement process.
     * @return Whether we are enhancing
     */
    public boolean isEnhancing()
    {
        return enhancing;
    }

    /**
     * Convenience method to return if the specified class is a known persistable class.
     * @param className Name of the class
     * @return Whether it is persistable
     */
    public boolean isClassPersistable(String className)
    {
        AbstractClassMetaData acmd = readMetaDataForClass(className);
        if (acmd == null)
        {
            return false;
        }
        return (acmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_CAPABLE);
    }

    /**
     * Accessor for all FileMetaData currently managed here.
     * @return FileMetaData managed here currently
     */
    public FileMetaData[] getFileMetaData()
    {
        Collection filemds = fileMetaDataByURLString.values();
        return (FileMetaData[])filemds.toArray(new FileMetaData[filemds.size()]);
    }

    /**
     * Accessor for the names of the classes with MetaData currently registered with this manager.
     * @return Names of classes with MetaData
     */
    public Collection getClassesWithMetaData()
    {
        return Collections.unmodifiableCollection(classMetaDataByClass.keySet());
    }

    /**
     * Convenience method to check if we have metadata present for the specified class.
     * @param className The name of the class to check
     * @return Whether the metadata is already registered for this class
     */
    public boolean hasMetaDataForClass(String className)
    {
        if (className == null)
        {
            return false;
        }

        // Check if this class has no MetaData before instantiating its class
        if (isClassWithoutPersistenceInfo(className))
        {
            return false;
        }
       
        return (classMetaDataByClass.get(className) != null);
    }


    /**
     * Accessor for whether a class doesn't have MetaData or annotations.
     * @param className Name of the class
     * @return Whether it has no metadata and annotations
     */
    protected boolean isClassWithoutPersistenceInfo(String className)
    {
        if (className == null)
        {
            return true;
        }

        // Standard Java classes have no MetaData
        if (className.startsWith("java.") || className.startsWith("javax."))
        {
            return true;
        }

        // Use the cache to determine if it has metadata
        return classesWithoutPersistenceInfo.contains(className);
    }


    /**
     * Accessor for the MetaData for a class given the name and a loader.
     * All MetaData returned from this method will be initialised and ready for full use.
     * If the class can't be loaded, null will be returned.
     * @param className Name of the class to find MetaData for
     * @param clr ClassLoaderResolver resolver for use in loading the class.
     * @return The ClassMetaData for this class (or null if not found)
     **/
    public synchronized AbstractClassMetaData getMetaDataForClass(String className, ClassLoaderResolver clr)
    {
        if (className == null)
        {
            return null;
        }

        // Check if this class has no MetaData/annotations before instantiating its class
        if (isClassWithoutPersistenceInfo(className))
        {
            return null;
        }

        // Resolve the class
        Class c = null;
        try
        {
            if (clr == null)
            {
                c = Class.forName(className);
            }
            else
            {
                c = clr.classForName(className, null, false);
            }
        }
        catch (ClassNotFoundException cnfe)
        {
        }
        catch (ClassNotResolvedException cnre)
        {
        }
        if (c == null)
        {
            return null;
        }

        return getMetaDataForClass(c, clr);
    }

    /** Temporary list of the FileMetaData objects utilised in this call for metadata. */
    protected ArrayList utilisedFileMetaData = new ArrayList();

    /**
     * Main accessor for the MetaData for a class.
     * All MetaData returned from this method will be initialised and ready for full use.
     * @param c The class to find MetaData for
     * @param clr the ClassLoaderResolver
     * @return The ClassMetaData for this class (or null if not found)
     */
    public synchronized AbstractClassMetaData getMetaDataForClass(Class c, ClassLoaderResolver clr)
    {
        if (c == null)
        {
            return null;
        }

        if(isClassWithoutPersistenceInfo(c.getName()))
        {
            return null;
        }

        AbstractClassMetaData cmd = null;
        if (c.isInterface())
        {
            // "persistent-interface" - check if it has class built at runtime and return the MetaData for it
            cmd = getClassMetaDataForImplementationOfPersistentInterface(c.getName());
        }
        else
        {
            // "persistent-class"
            cmd = getMetaDataForClassInternal(c, clr);
        }

        if (cmd != null)
        {
            // Make sure that anything returned is initialised
            if (!cmd.isPopulated())
            {
                // Initialise the class in question
                cmd.populate();
            }
            if (!cmd.isInitialised())
            {
                // Initialise the class in question
                cmd.initialise();
            }

            if (utilisedFileMetaData.size() > 0)
            {
                // Initialise all FileMetaData that were processed in this call
                Iterator iter = utilisedFileMetaData.iterator();
                while (iter.hasNext())
                {
                    FileMetaData filemd = (FileMetaData)iter.next();
                    initialiseFileMetaData(filemd, clr,c.getClassLoader());
                }
            }
        }
        else
        {
            if (!c.isInterface())
            {
                classesWithoutPersistenceInfo.add(c.getName());
            }
        }
        utilisedFileMetaData.clear();
        return cmd;
    }

    /**
     * Accessor for the MetaData for a class given the "entity-name".
     * @param entityName The entity name to find MetaData for
     * @return The ClassMetaData for this entity name (or null if not found)
     */
    public synchronized AbstractClassMetaData getMetaDataForEntityName(String entityName)
    {
        return (AbstractClassMetaData) classMetaDataByEntityName.get(entityName);
    }

    /**
     * Method to access the (already known) metadata for the specified class.
     * If the class is not yet known about it returns null.
     * @param className Name of the class
     * @return MetaData for the class
     */
    public AbstractClassMetaData readMetaDataForClass(String className)
    {
        return (AbstractClassMetaData)classMetaDataByClass.get(className);
    }

    /**
     * Method to access the (already known) metadata for the field/property of the specified class.
     * If the class (or this field/property) is not yet known about it returns null.
     * @param className Name of the class
     * @param memberName Name of the field/property
     * @return MetaData for the field/property
     */
    public AbstractMemberMetaData readMetaDataForMember(String className, String memberName)
    {
        AbstractClassMetaData cmd = readMetaDataForClass(className);
        return (cmd != null ? cmd.getMetaDataForMember(memberName) : null);
    }

    /**
     * Internal convenience method for accessing the MetaData for a class.
     * MetaData returned by this method may be uninitialised so should only really
     * be used in initialisation processes.
     * To be implemented by the implementing class.
     * @param c The class to find MetaData for
     * @return The ClassMetaData for this class (or null if not found)
     **/
    public abstract AbstractClassMetaData getMetaDataForClassInternal(Class c, ClassLoaderResolver clr);

    /**
     * Internal method called when we want to register the metadata for a class/interface.
     * @param fullClassName Name of the class
     * @param cmd The metadata
     */
    protected void registerMetaDataForClass(String fullClassName, AbstractClassMetaData cmd)
    {
        classMetaDataByClass.put(fullClassName, cmd);

        // invalidate our cache of subclass information
        directSubclassesByClass.clear();
    }

    /**
     * Accessor for the subclasses of a particular class
     * @param className Name of the class that we want the known subclasses for.
     * @param includeDescendents Whether to include subclasses of subclasses etc
     * @return Names of the subclasses. return null if there are no subclasses
     */
    public String[] getSubclassesForClass(String className, boolean includeDescendents)
    {
        Collection subclassNames = new HashSet();
        provideSubclassesForClass(className, includeDescendents, subclassNames);
        if (subclassNames.size() > 0)
        {
            return (String[])subclassNames.toArray(new String[subclassNames.size()]);
        }

        return null;
    }

    /**
     * Provide the subclasses of a particular class to a given <code>consumer</code>
     * @param className Name of the class that we want the known subclasses for.
     * @param includeDescendents Whether to include subclasses of subclasses etc
     * @param consumer the Collection (Set) where discovered subclasses are added
     */
    private void provideSubclassesForClass(String className, boolean includeDescendents, Collection consumer)
    {
        // make use of cached subclass information or we have quadratic complexity here
        Set directSubClasses = (Set) directSubclassesByClass.get(className);
        if (directSubClasses == null)
        {
            directSubClasses = computeDirectSubclassesForClass(className);
            directSubclassesByClass.put(className, directSubClasses);
        }
        consumer.addAll(directSubClasses);

        if (includeDescendents)
        {
            Iterator subClassNameIter = directSubClasses.iterator();
            while (subClassNameIter.hasNext())
            {
                //go deeper in subclasses
                provideSubclassesForClass((String)subClassNameIter.next(), includeDescendents, consumer);
            }
        }
    }

    /**
     * Calculate the subclasses of a particular class.
     * Runs in O(n) of the number of all known classes
     * @param className Name of the class to find subclasses for
     * @return Set<ClassMetaData>
     */
    private Set computeDirectSubclassesForClass(String className)
    {
        Set result = new HashSet();
        Collection cmds = classMetaDataByClass.values();
        Iterator cmdIter = cmds.iterator();
        while (cmdIter.hasNext())
        {
            AbstractClassMetaData acmd = (AbstractClassMetaData)cmdIter.next();
            if (acmd instanceof ClassMetaData)
            {
                ClassMetaData cmd = (ClassMetaData)acmd;
                if (cmd.getPersistenceCapableSuperclass() != null &&
                    cmd.getPersistenceCapableSuperclass().equals(className))
                {
                    result.add(cmd.getFullClassName());
                }
            }
        }
        return result;
    }

    /**
     * Accessor for the list of names of classes that are declared to implement the specified interface
     * (using &lt;implements&gt; in the MetaData). This will include subclasses of declared classes. Ignore abstract classes.
     * The array of implementation class names will have the initial implementations first followed by
     * the subclass implementations etc. So for example if we look for all implementations of I and A implements I
     * and B extends A, then it will return [A, B] in that order.
     * @param interfaceName Name of the interface
     * @param clr The ClassLoaderResolver
     * @return The names of the classes declared as implementing that interface. return null if no classes
     */
    public String[] getClassesImplementingInterface(String interfaceName, ClassLoaderResolver clr)
    {
        Collection classes = new HashSet();
        Class intfClass = clr.classForName(interfaceName);

        // Loop through all known classes and find the implementations
        // TODO This completely ignores the <implements> keyword info and should use it
        Collection cmds = classMetaDataByClass.values();
        Iterator cmdIter = cmds.iterator();
        boolean isPersistentInterface = false;
        while (cmdIter.hasNext())
        {
            AbstractClassMetaData acmd = (AbstractClassMetaData)cmdIter.next();
            Class implClass = clr.classForName(acmd.getFullClassName());
            if (acmd instanceof ClassMetaData)
            {
                if (!acmd.isInitialised())
                {
                    // Make sure that we are initialised since implementsMetaData wont be set
                    acmd.initialise();
                }
                if (intfClass.isAssignableFrom(implClass))
                {
                    if (!((ClassMetaData)acmd).isAbstractPersistenceCapable())
                    {
                        classes.add(implClass);
                    }
                }
            }
            else if (acmd instanceof InterfaceMetaData)
            {
                if (intfClass.isAssignableFrom(implClass))
                {
                    isPersistentInterface = true;
                }
            }
        }

        if (isPersistentInterface)
        {
            // JDO2 "persistent interfaces"
            // This deliberately kept separate from normal persistence since it is largely undocumented and best left alone
            //TODO this is very time consuming. got to do some cache
            classes.add(omfContext.getImplementationCreator().newInstance(intfClass, this, clr).getClass());

            String[] classNames = new String[classes.size()];
            Iterator iter = classes.iterator();
            int i = 0;
            while (iter.hasNext())
            {
                classNames[i++] = ((Class)iter.next()).getName();
            }
            return classNames;
        }
        else if (classes.size() > 0)
        {
            // Normal persistence
            // Put the classes into a sorter so we make sure we get the initial implementations first followed
            // by any subclasses of these implementations. This is needed because when generating the schema we require
            // the subclass implementations to already have their datastore column created
            Collection classesSorted = new TreeSet(new InterfaceClassComparator());
            Iterator classesIter = classes.iterator();
            while (classesIter.hasNext())
            {
                classesSorted.add(classesIter.next());
            }

            // Return the class names (in the same order)
            String[] classNames = new String[classesSorted.size()];
            Iterator iter = classesSorted.iterator();
            int i = 0;
            while (iter.hasNext())
            {
                classNames[i++] = ((Class)iter.next()).getName();
            }
            return classNames;
        }
        return null;
    }

    /**
     * Simple comparator that orders the implementations of an interface so that the initial implementations
     * are first, and the subclasses later.
     */
    private class InterfaceClassComparator implements Comparator
    {
        /**
         * Default constructor.
         */
        public InterfaceClassComparator()
        {
            // Nothing to do
        }

        /**
         * Method defining the ordering of objects.
         * Places all nulls at the end.
         * @param o1 First object
         * @param o2 Second object
         * @return The comparison result
         */
        public int compare(Object o1, Object o2)
        {
            if (o1 == null && o2 == null)
            {
                return 0;
            }
            else if (o1 == null || o2 == null)
            {
                return Integer.MIN_VALUE;
            }

            // Just order based on hashcode
            Class cls1 = (Class)o1;
            Class cls2 = (Class)o2;
            return cls1.hashCode() - cls2.hashCode();
        }
    }

    /**
     * Load up and add any O/R mapping info for the specified class to the stored ClassMetaData (if supported).
     * This implementation does nothing so if ORM files are supported then this should be overridden by subclasses.
     * Is package-access so that is only accessable by MetaData classes
     * @param c The class
     * @param clr ClassLoader resolver
     */
    protected void addORMDataToClass(Class c, ClassLoaderResolver clr)
    {
        // Default to doing nothing. Specified in subclasses if they support it
        return;
    }

    /**
     * Load up and add any annotations mapping info for the specified class to the stored ClassMetaData.
     * Is package-access so that is only accessable by MetaData classes
     * @param c The class
     * @param cmd the metadata to add annotation to
     * @param clr ClassLoader resolver
     */
    void addAnnotationsDataToClass(Class c, AbstractClassMetaData cmd, ClassLoaderResolver clr)
    {
        // Get the MetaData for this class/interface
        if (cmd.getPackageMetaData() != null && cmd.getPackageMetaData().getFileMetaData() != null &&
            cmd.getPackageMetaData().getFileMetaData().getType() == FileMetaData.ANNOTATIONS)
        {
            // Our MetaData is derived from the Annotations so nothing to merge!
            return;
        }

        // Find if there is any annotations metadata available
        FileMetaData filemd = loadAnnotationsForClass(c, clr, false, false);
        if (filemd != null)
        {
            AbstractClassMetaData annotCmd = filemd.getPackage(0).getClass(0);
            if (annotCmd != null)
            {
                // Merge the annotations MetaData into the class MetaData
                MetaDataMerger.mergeClassAnnotationsData(cmd, annotCmd);
            }
        }
    }

    /**
     * Accessor for the MetaData for an implementation of a reference type.
     * Finds the metadata for the implementation of this reference.
     * @param referenceClass The reference class to find MetaData for
     * @param implValue Object of an implementation class, to return if possible (null=ignore)
     * @param clr ClassLoader resolver
     * @return The ClassMetaData for an implementation of a reference type
     */
    public ClassMetaData getMetaDataForImplementationOfReference(Class referenceClass, Object implValue, ClassLoaderResolver clr)
    {
        if (referenceClass == null || (!referenceClass.isInterface() && referenceClass != java.lang.Object.class))
        {
            return null;
        }

        // Check if this is a "persistent interface"
        Object intfMetaData = getClassMetaDataForImplementationOfPersistentInterface(referenceClass.getName());
        if (intfMetaData != null)
        {
            return (ClassMetaData)intfMetaData;
        }

        ClassMetaData cmd = null;

        // Search for the class required
        Set classMetaDataClasses = classMetaDataByClass.keySet();
        Iterator classMetaDataClassesIter = classMetaDataClasses.iterator();
        while (classMetaDataClassesIter.hasNext())
        {
            String class_name = (String)classMetaDataClassesIter.next();
            AbstractClassMetaData acmd_cls = (AbstractClassMetaData)classMetaDataByClass.get(class_name);

            if (acmd_cls instanceof ClassMetaData)
            {
                try
                {
                    // Check if class is implementation of "implValue" (in the case of java.lang.Object, all will be!)
                    Class cls = referenceClass.getClassLoader().loadClass(class_name);
                    if (referenceClass.isAssignableFrom(cls))
                    {
                        // Find the base class that is an implementation
                        cmd = (ClassMetaData)acmd_cls;
                        if (implValue != null && cmd.getFullClassName().equals(implValue.getClass().getName()))
                        {
                            return cmd;
                        }

                        AbstractClassMetaData cmd_superclass = cmd.getSuperAbstractClassMetaData();
                        while (cmd_superclass != null)
                        {
                            if (!referenceClass.isAssignableFrom(clr.classForName(((ClassMetaData)cmd_superclass).getFullClassName())))
                            {
                                break;
                            }
                            // TODO Check if superclass is an implementation
                            cmd = (ClassMetaData) cmd_superclass;
                            if (implValue != null && cmd.getFullClassName().equals(implValue.getClass().getName()))
                            {
                                break;
                            }

                            // Go to next superclass
                            cmd_superclass = cmd_superclass.getSuperAbstractClassMetaData();
                            if (cmd_superclass == null)
                            {
                                break;
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                }
            }
        }

        return cmd;
    }

    /**
     * Accessor for the MetaData for a field/property of a class.
     * Utilises getMetaDataForClass, and then finds the relevant field/property.
     * @param className The name of the class owning the field/property
     * @param memberName The name of the field to find MetaData for
     * @param clr ClassLoaderResolver resolver for any loading of classes
     * @return The metadata for this field/property (or null if not found)
     */
    public AbstractMemberMetaData getMetaDataForMember(String className, String memberName, ClassLoaderResolver clr)
    {
        if (className == null || memberName == null)
        {
            return null;
        }

        AbstractClassMetaData cmd = getMetaDataForClass(className, clr);
        return (cmd != null ? cmd.getMetaDataForMember(memberName) : null);
    }

    /**
     * Accessor for the MetaData for a field/property of a class.
     * Utilises getMetaDataForClass, and then finds the relevant field/property.
     * @param c The class owning the field/property
     * @param clr the ClassLoaderResolver
     * @param memberName The name of the field/property to find MetaData for
     * @return The metadata for this field/property (or null if not found)
     */
    public AbstractMemberMetaData getMetaDataForMember(Class c, ClassLoaderResolver clr, String memberName)
    {
        if (c == null || memberName == null)
        {
            return null;
        }

        AbstractClassMetaData cmd = getMetaDataForClass(c, clr);
        return (cmd != null ? cmd.getMetaDataForMember(memberName) : null);
    }

    /**
     * Accessor for the MetaData for a named query for a class.
     * If the class is not specified, searches for the query with this name for any class.
     * Will only return metadata for queries already registered in this implementation.
     * @param cls The class which has the query defined for it
     * @param clr the ClassLoaderResolver
     * @param queryName Name of the query
     * @return The QueryMetaData for the query for this class
     **/
    public QueryMetaData getMetaDataForQuery(Class cls, ClassLoaderResolver clr, String queryName)
    {
        if (queryName == null)
        {
            return null;
        }

        String query_key = queryName;
        if (cls != null)
        {
            query_key = cls.getName() + "_" + queryName;
        }
        return (QueryMetaData)queryMetaDataByName.get(query_key);
    }

    /**
     * Accessor for the MetaData for a named fetch plan.
     * @param name Name of the fetch plan
     * @return The FetchPlanMetaData for this name (if any)
     **/
    public FetchPlanMetaData getMetaDataForFetchPlan(String name)
    {
        if (name == null)
        {
            return null;
        }

        return (FetchPlanMetaData)fetchPlanMetaDataByName.get(name);
    }

    /**
     * Accessor for the MetaData for a Sequence in a package.
     * This implementation simply checks what is already loaded and returns if found
     * @param clr the ClassLoaderResolver
     * @param seqName Name of the package (fully qualified if necessary)
     * @return The SequenceMetaData for this named sequence
     **/
    public SequenceMetaData getMetaDataForSequence(ClassLoaderResolver clr, String seqName)
    {
        if (seqName == null)
        {
            return null;
        }

        return (SequenceMetaData)sequenceMetaDataByPackageSequence.get(seqName);
    }

    /**
     * Accessor for the MetaData for a TableGenerator in a package.
     * This implementation simply checks what is already loaded and returns if found
     * @param clr the ClassLoaderResolver
     * @param genName Name of the package (fully qualified if necessary)
     * @return The TableGenerator for this named generator
     **/
    public TableGeneratorMetaData getMetaDataForTableGenerator(ClassLoaderResolver clr, String genName)
    {
        if (genName == null)
        {
            return null;
        }

        return (TableGeneratorMetaData)tableGeneratorMetaDataByPackageSequence.get(genName);
    }

    /**
     * Accessor for the MetaData for a QueryResult.
     * @param name Name of the query result
     * @return The QueryResultMetaData under this name
     **/
    public QueryResultMetaData getMetaDataForQueryResult(String name)
    {
        if (name == null)
        {
            return null;
        }

        return (QueryResultMetaData)queryResultMetaDataByName.get(name);
    }

    // ------------------------------- Persistent Interfaces ---------------------------------------

    /**
     * Accessor for the MetaData for an interface.
     * Part of the support for "persistent-interface".
     * This defaults to returning null since interfaces are only supported by JDO.
     * @param c The interface to find MetaData for
     * @param clr the ClassLoaderResolver
     * @return The InterfaceMetaData for this interface (or null if not found)
     */
    public InterfaceMetaData getMetaDataForInterface(Class c, ClassLoaderResolver clr)
    {
        return null;
    }

    /**
     * Convenience method to return if the passed class name is a "persistent-interface".
     * @param name Name if the interface
     * @return Whether it is a "persistent-interface"
     */
    public boolean isPersistentInterface(String name)
    {
        // Default to not supporting "persistent-interface"s
        return false;
    }

    /**
     * Convenience method to return if the passed class name is an implementation of the passed "persistent-interface".
     * @param interfaceName Name of the persistent interface
     * @param implName The implementation name
     * @return Whether it is a (JPOX-generated) impl of the persistent interface
     */
    public boolean isPersistentInterfaceImplementation(String interfaceName, String implName)
    {
        // Default to not supporting "persistent-interface"s
        return false;
    }

    /**
     * Convenience method to return if the passed class name is an implementation of a "persistent definition".
     * @param implName The implementation name
     * @return Whether it is a (JPOX-generated) impl of the persistent interface or abstract class
     */
    public boolean isPersistentDefinitionImplementation(String implName)
    {
        return false;
    }

    /**
     * Accessor for the implementation name for the specified "persistent-interface".
     * @param interfaceName The name of the persistent interface
     * @return The name of the implementation class
     */
    public String getImplementationNameForPersistentInterface(String interfaceName)
    {
        // Default to not supporting "persistent-interface"s
        return null;
    }

    /**
     * Accessor for the metadata for the implementation of the specified "persistent-interface".
     * @param interfaceName The name of the persistent interface
     * @return The ClassMetaData of the implementation class
     */
    public ClassMetaData getClassMetaDataForImplementationOfPersistentInterface(String interfaceName)
    {
        // Default to not supporting "persistent-interface"s
        return null;
    }

    /**
     * Method to register a persistent interface and its implementation with the MetaData system.
     * @param imd MetaData for the interface
     * @param implClass The implementation class
     * @param clr ClassLoader Resolver to use
     */
    public void registerPersistentInterface(InterfaceMetaData imd, Class implClass, ClassLoaderResolver clr)
    {
        // Default to not supporting "persistent-interface"s
        return;
    }

    /**
     * Method to register the metadata for an implementation of a persistent abstract class.
     * @param cmd MetaData for the abstract class
     * @param implClass The implementation class
     * @param clr ClassLoader resolver
     */
    public void registerImplementationOfAbstractClass(ClassMetaData cmd, Class implClass, ClassLoaderResolver clr)
    {
        // Default to not supporting "persistent-abstract-classes"
        return;
    }

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

    /**
     * Method to parse all available "persistence.xml" files and return the metadata
     * for the persistence unit with the specified name.
     * @param unitName Name of the persistence-unit
     * @return MetaData for the persistence-unit of the specified name (or null if not found)
     * @throws JPOXUserException if no "persistence.xml" files are found
     */
    public PersistenceUnitMetaData getMetaDataForPersistenceUnit(String unitName)
    {
        PersistenceFileMetaData[] files = parsePersistenceFiles();
        if (files == null)
        {
            // No "persistence.xml" files found
            throw new JPOXUserException(LOCALISER.msg("044046"));
        }
        else
        {
            for (int i=0;i<files.length;i++)
            {
                PersistenceUnitMetaData[] unitmds = files[i].getPersistenceUnits();
                if (unitmds != null)
                {
                    for (int j=0;j<unitmds.length;j++)
                    {
                        if (unitmds[j].getName().equals(unitName))
                        {
                            // Found the required unit
                            return unitmds[j];
                        }
                    }
                }
            }
        }
        return null;
    }

    /**
     * Method to parse the available "persistence.xml" files returning the metadata for all found.
     * Searches for all files "META-INF/persistence.xml" in the CLASSPATH of the current thread.
     * @return The metadata for all "persistence.xml" files
     */
    public PersistenceFileMetaData[] parsePersistenceFiles()
    {
        String filename = omfContext.getPersistenceConfiguration().getStringProperty("org.jpox.persistenceXmlFilename");
        if (filename != null)
        {
            // User has specified filename for persistence.xml via property "datanucleus.persistenceXmlFilename"
            try
            {
                URL fileURL = new URL(filename);
                if (metaDataParser == null)
                {
                    new MetaDataParser(this, validateMetaData);
                }
                MetaData permd = metaDataParser.parseMetaDataURL(fileURL, "persistence");
                return new PersistenceFileMetaData[] {(PersistenceFileMetaData)permd};
            }
            catch (MalformedURLException mue)
            {
                // User provided file is not found
                JPOXLogger.METADATA.error("Error reading user-specified persistence.xml file " + filename, mue);
            }
        }

        HashSet metadata = new HashSet();
        ClassLoaderResolver clr = omfContext.getClassLoaderResolver(null);
        try
        {
            // Find all "META-INF/persistence.xml" files in the CLASSPATH of the current thread
            Enumeration files = clr.getResources("META-INF/persistence.xml",
                Thread.currentThread().getContextClassLoader());
            if (!files.hasMoreElements())
            {
                return null;
            }

            if (metaDataParser == null)
            {
                metaDataParser = new MetaDataParser(this, validateMetaData);
            }
            for ( ; files.hasMoreElements() ;)
            {
                // Parse the "persistence.xml"
                URL fileURL = (URL)files.nextElement();
                MetaData permd = metaDataParser.parseMetaDataURL(fileURL, "persistence");
                metadata.add(permd);
            }
        }
        catch (IOException ioe)
        {
            // Do nothing
            JPOXLogger.METADATA.warn(StringUtils.getStringFromStackTrace(ioe));
        }

        return (PersistenceFileMetaData[])metadata.toArray(new PersistenceFileMetaData[metadata.size()]);
    }

    /**
     * Utility to parse a MetaData file.
     * @param file_url URL of the file
     * @return The FileMetaData for this file
     */
    protected abstract FileMetaData parseFile(URL file_url);

    /**
     * Method to take the FileMetaData and register the relevant parts of it with the assorted caches provided.
     * @param fileURLString URL of the metadata file
     * @param filemd The File MetaData
     */
    public abstract void registerFile(String fileURLString, FileMetaData filemd, ClassLoaderResolver clr);

    /**
     * Convenience method to register all sequences found in the passed file.
     * @param filemd MetaData for the file
     */
    protected void registerSequencesForFile(FileMetaData filemd)
    {
        // Register all sequences for the packages in this file
        for (int i=0;i<filemd.getNoOfPackages();i++)
        {
            PackageMetaData pmd = filemd.getPackage(i);
            SequenceMetaData[] seqmds = pmd.getSequences();
            if (seqmds != null)
            {
                for (int j=0;j<seqmds.length;j++)
                {
                    // Register using its fully qualified name (JDO)
                    sequenceMetaDataByPackageSequence.put(seqmds[j].getFullyQualifiedName(), seqmds[j]);
                }
            }
        }
    }

    /**
     * Convenience method to register all table generators found in the passed file.
     * @param filemd MetaData for the file
     */
    protected void registerTableGeneratorsForFile(FileMetaData filemd)
    {
        // Register all table generators for the packages in this file
        for (int i=0;i<filemd.getNoOfPackages();i++)
        {
            PackageMetaData pmd = filemd.getPackage(i);
            TableGeneratorMetaData[] tgmds = pmd.getTableGenerators();
            if (tgmds != null)
            {
                for (int j=0;j<tgmds.length;j++)
                {
                    // Register using its fully qualified name (JDO)
                    tableGeneratorMetaDataByPackageSequence.put(tgmds[j].getFullyQualifiedName(), tgmds[j]);
                }
            }
        }
    }

    /**
     * Convenience method to register all table generators found in the passed file.
     * @param filemd MetaData for the file
     */
    protected void registerQueryResultMetaDataForFile(FileMetaData filemd)
    {
        // Register all query result mappings for the file
        QueryResultMetaData[] fqrmds = filemd.getQueryResultMetaData();
        if (fqrmds != null)
        {
            for (int i=0;i<fqrmds.length;i++)
            {
                queryResultMetaDataByName.put(fqrmds[i].getName(), fqrmds[i]);
            }
        }

        // Register all query result mappings for the classes in the file
        for (int i=0;i<filemd.getNoOfPackages();i++)
        {
            PackageMetaData pmd = filemd.getPackage(i);
            for (int j=0;j<pmd.getNoOfClasses();j++)
            {
                AbstractClassMetaData cmd = pmd.getClass(j);
                QueryResultMetaData[] qrmds = cmd.getQueryResultMetaData();
                if (qrmds != null)
                {
                    for (int k=0;k<qrmds.length;k++)
                    {
                        queryResultMetaDataByName.put(qrmds[k].getName(), qrmds[k]);
                    }
                }
            }
        }
    }

    /**
     * Convenience method to register all queries found in the passed file.
     * @param filemd MetaData for the file
     */
    protected void registerQueriesForFile(FileMetaData filemd)
    {
        // Register all queries for this file
        // Store queries against "queryname"
        QueryMetaData[] queries = filemd.getQueries();
        if (queries != null)
        {
            for (int i=0;i<queries.length;i++)
            {
                String scope = queries[i].getScope();
                String key = queries[i].getName();
                if (scope != null)
                {
                    key = scope + "_" + key;
                }
                queryMetaDataByName.put(key, queries[i]);
            }
        }

        for (int i = 0; i < filemd.getNoOfPackages(); i++)
        {
            PackageMetaData pmd = filemd.getPackage(i);

            // Register all classes (and their queries) into the respective lookup maps
            for (int j = 0; j < pmd.getNoOfClasses(); j++)
            {
                // Store queries against "classname_queryname"
                ClassMetaData cmd = pmd.getClass(j);
                QueryMetaData[] classQueries = cmd.getQueries();
                if (classQueries != null)
                {
                    for (int k = 0; k < classQueries.length; k++)
                    {
                        String scope = classQueries[k].getScope();
                        String key = classQueries[k].getName();
                        if (scope != null)
                        {
                            key = scope + "_" + key;
                        }
                        queryMetaDataByName.put(key, classQueries[k]);
                    }
                }
            }

            // Register all interfaces (and their queries) into the respective lookup maps
            for (int j = 0; j < pmd.getNoOfInterfaces(); j++)
            {
                // Store queries against "classname_queryname"
                InterfaceMetaData intfmd = pmd.getInterface(j);
                QueryMetaData[] interfaceQueries = intfmd.getQueries();
                if (interfaceQueries != null)
                {
                    for (int k = 0; k < interfaceQueries.length; k++)
                    {
                        String scope = interfaceQueries[k].getScope();
                        String key = interfaceQueries[k].getName();
                        if (scope != null)
                        {
                            key = scope + "_" + key;
                        }
                        queryMetaDataByName.put(key, interfaceQueries[k]);
                    }
                }
            }
        }
    }

    /**
     * Convenience method to register all FetchPlans found in the passed file.
     * @param filemd MetaData for the file
     */
    protected void registerFetchPlansForFile(FileMetaData filemd)
    {
        // Register all queries for this file
        // Store queries against "queryname"
        FetchPlanMetaData[] fetchPlans = filemd.getFetchPlans();
        if (fetchPlans != null)
        {
            for (int i=0;i<fetchPlans.length;i++)
            {
                fetchPlanMetaDataByName.put(fetchPlans[i].getName(), fetchPlans[i]);
            }
        }
    }

    /**
     * Convenience method to populate all classes/interfaces in a Meta-Data file.
     * @param filemd The MetaData file
     * @param clr Class Loader to use in population
     * @param primary the primary ClassLoader to use (or null)
     */
    protected void populateFileMetaData(FileMetaData filemd, ClassLoaderResolver clr, ClassLoader primary)
    {
        for (int i=0;i<filemd.getNoOfPackages();i++)
        {
            PackageMetaData pmd = filemd.getPackage(i);
            for (int j=0;j<pmd.getNoOfClasses();j++)
            {
                AbstractClassMetaData cmd = pmd.getClass(j);
                if (!cmd.isPopulated() && !cmd.isInitialised())
                {
                    cmd.populate(clr, primary);
                }
            }
            for (int j=0;j<pmd.getNoOfInterfaces();j++)
            {
                AbstractClassMetaData cmd = pmd.getInterface(j);
                if (!cmd.isPopulated() && !cmd.isInitialised())
                {
                    cmd.populate(clr, primary);
                }
            }
        }
    }

    /**
     * Initialise all classes/interfaces in a Meta-Data file.
     * @param filemd the FileMetaData
     * @param clr ClassLoader resolver to use
     * @param primary the primary ClassLoader to use (or null)
     */
    protected void initialiseFileMetaData(FileMetaData filemd, ClassLoaderResolver clr, ClassLoader primary)
    {
        for (int i=0;i<filemd.getNoOfPackages();i++)
        {
            PackageMetaData pmd = filemd.getPackage(i);
            for (int j=0;j<pmd.getNoOfClasses();j++)
            {
                ClassMetaData cmd = pmd.getClass(j);
                try
                {
                    initialiseClassMetaData(cmd, clr.classForName(cmd.getFullClassName(),primary), clr);
                }
                catch(JPOXException jpex)
                {
                    throw jpex;
                }
                catch (RuntimeException re)
                {
                    // Do nothing
                }
            }

            for (int j=0;j<pmd.getNoOfInterfaces();j++)
            {
                InterfaceMetaData imd = pmd.getInterface(j);
                try
                {
                    initialiseInterfaceMetaData(imd, clr, primary);
                }
                catch(JPOXException jpex)
                {
                    throw jpex;
                }
                catch (RuntimeException re)
                {
                    // Do nothing
                }
            }
        }
    }

    /**
     * Utility to initialise the MetaData for a class, using the specified
     * class. This assigns defaults to tags that haven't been assigned.
     * If the class that is being used to populate the MetaData is not
     * enhanced, this will throw a JPOXUserException informing them of this.
     * @param cmd The classes metadata
     * @param cls The class to use as a basis for initialisation
     * @param clr ClassLoader resolver to use
     * @throws JPOXUserException if the class is not enhanced
     */
    protected void initialiseClassMetaData(ClassMetaData cmd, Class cls, ClassLoaderResolver clr)
    {
        synchronized(cmd)
        {
            if (!enhancing && cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_CAPABLE &&
                !getOMFContext().getApiAdapter().isPersistable(cls))
            {
                throw new JPOXUserException(LOCALISER.msg("044059", cls.getName()));
            }
            if (!cmd.isPopulated() && !cmd.isInitialised())
            {
                cmd.populate(clr, cls.getClassLoader());
            }
            if (!cmd.isInitialised())
            {
                cmd.initialise();
            }
        }
    }

    /**
     * Utility to initialise the MetaData for a interface, using the specified
     * class. This assigns defaults to tags that haven't been assigned.
     * If the class that is being used to populate the MetaData is not
     * enhanced, this will throw a JPOXUserException informing them of this.
     * @param imd The interface metadata
     * @param clr The loader of the interface
     * @param primary the primary ClassLoader to use (or null)
     */
    protected void initialiseInterfaceMetaData(InterfaceMetaData imd, ClassLoaderResolver clr, ClassLoader primary)
    {
        synchronized(imd)
        {
            if (!imd.isPopulated() && !imd.isInitialised())
            {
                imd.populate(clr, primary);
            }
            if (!imd.isInitialised())
            {
                imd.initialise();
            }
        }
    }

    /**
     * Method to load the annotations for the specified class and return the FileMetaData containing
     * the class. The FileMetaData, PackageMetaData will be dummy records.
     * @param cls The class
     * @param clr ClassLoader resolver
     * @param register Whether to register the data
     * @param populate Whether to populate the data
     * @return The FileMetaData
     */
    protected FileMetaData loadAnnotationsForClass(Class cls, ClassLoaderResolver clr, boolean register, boolean populate)
    {
        if (annotationManager != null)
        {
            if (isClassWithoutPersistenceInfo(cls.getName()))
            {
                return null;
            }

            String clsPackageName = ClassUtils.getPackageNameForClass(cls);
            if (clsPackageName == null)
            {
                // Unknown type with no package, so perhaps so multiple array (int[][] or something)
                return null;
            }

            // Check for annotations (use dummy file/package so we have a place for it)
            FileMetaData filemd = new FileMetaData(null, this, null, null);
            filemd.setType(FileMetaData.ANNOTATIONS);
            PackageMetaData pmd = new PackageMetaData(filemd, clsPackageName, null, null);
            filemd.addPackage(pmd);
            AbstractClassMetaData cmd = annotationManager.getMetaDataForClass(cls, pmd, clr);
            if (cmd != null)
            {
                if (register)
                {
                    // register before populating to avoid recursive loops when loading referenced classes
                    registerFile("annotations:" + cls.getName(), filemd, clr);

                    if (populate)
                    {
                        // Populate all classes in this file we've just parsed (i.e only 1!)
                        populateFileMetaData(filemd, clr, cls.getClassLoader());
                    }
                }
                return filemd;
            }
        }
        return null;
    }

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

    /**
     * Utility to return all ClassMetaData that is referenced from the supplier
     * class.
     * @param cmd The origin class's MetaData.
     * @param dba_vendor_id The Vendor id of the database adapter in use.
     *                      (Used in handling "views" support)
     * @param clr ClassLoaderResolver resolver for loading any classes.
     * @return List of ClassMetaData referenced by the origin
     */
    public List getReferencedClassMetaData(AbstractClassMetaData cmd,
                                           String dba_vendor_id,
                                           ClassLoaderResolver clr)
    {
        if (cmd == null)
        {
            return null;
        }

        List orderedCMDs=new ArrayList();
        Set referencedCMDs=new HashSet();

        // Use the ClassMetaData to tell us about its classes
        cmd.getReferencedClassMetaData(orderedCMDs,referencedCMDs,dba_vendor_id,clr);

        return orderedCMDs;
    }
   
    /**
     * Register to persistent class load
     */
    private class MetaDataRegisterClassListener implements RegisterClassListener
    {
        public void registerClass(RegisterClassEvent arg0)
        {
            // register the class / interface in metadata
            getMetaDataForClassInternal(arg0.getRegisteredClass(),
                omfContext.getClassLoaderResolver(arg0.getRegisteredClass().getClassLoader()));
        }
    }

   
    /**
     * Get the event listeners
     * @return the event listeners
     */
    public List getListeners()
    {
        return listeners;
    }

    /**
     * Method to set that the Manager is enhancing classes.
     * This implies using slightly weaker checks during read/populate/initialise of MetaData.
     */
    public void setEnhancing()
    {
        this.enhancing = true;
    }

    /**
     * Method to set that the Manager is not enhancing classes.
     * This implies reverting to the normal checks during read/populate/initialise of MetaData.
     */
    public void unsetEnhancing()
    {
        this.enhancing = false;
    }
   
    /**
     * Accessor for a factory for MetaData objects.
     * @return The factory of objects
     */
    public MetaDataFactory getMetaDataFactory()
    {
        if (metaDataFactory == null)
        {
            metaDataFactory = new DefaultMetaDataFactory();
        }
        return metaDataFactory;
    }

    /**
     * Method to set the Factory for MetaData objects.
     * This is used to create Class, Field, Property, Interface objects.
     * @param factory The factory
     */
    public void setMetaDataFactory(MetaDataFactory factory)
    {
        this.metaDataFactory = factory;
    }
}
TOP

Related Classes of org.jpox.metadata.MetaDataManager$InterfaceClassComparator

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.