Package org.jpox.jdo.metadata

Source Code of org.jpox.jdo.metadata.JDOMetaDataManager

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

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

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

Contributors:
    ...
**********************************************************************/
package org.jpox.jdo.metadata;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import org.jpox.ClassLoaderResolver;
import org.jpox.OMFContext;
import org.jpox.exceptions.JPOXException;
import org.jpox.metadata.AbstractClassMetaData;
import org.jpox.metadata.ClassMetaData;
import org.jpox.metadata.FileMetaData;
import org.jpox.metadata.ImplementsMetaData;
import org.jpox.metadata.InterfaceMetaData;
import org.jpox.metadata.MetaDataManager;
import org.jpox.metadata.MetaDataMerger;
import org.jpox.metadata.PackageMetaData;
import org.jpox.metadata.QueryMetaData;
import org.jpox.metadata.SequenceMetaData;
import org.jpox.metadata.xml.MetaDataParser;
import org.jpox.util.JPOXLogger;
import org.jpox.util.StringUtils;

/**
* Manager of JDO MetaData information in JPOX.
* <P>
* Acts as a registry of JDO 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>
* When the MetaData is requested for a class, if it isnt already found, the valid
* file locations are checked for that class and the file containing it will be
* read. The MetaData for all classes, queries, sequences etc in that file are
* loaded at that point. In addition, all classes will be "populated" (meaning that
* their superclasses are assigned, and unspecified fields are added, and any related
* objects are linked). The MetaData of these classes are only initialised when
* they are absolutely needed - to avoid generating circular references in the
* initialisation process.
* </P>
* <P>
* Each PMFContext typically will have its own MetaDataManager so allowing
* Meta-Data to be for different datastores. In addition, each PMF can allow
* MetaData files to use a particular suffix, hence we allow the JDO/ORM file
* suffices to be specifiable at construction.
* </P>
*
* @version $Revision: 1.31 $
*/
public class JDOMetaDataManager extends MetaDataManager
{
    /** MetaData files will be searched in all possible locations defined in JDO 1.0, JDO 1.0.1, JDO 2.0 or later **/
    public static final int ALL_JDO_LOCATIONS = 1;

    /** MetaData files will be searched in all locations defined in JDO 1.0 **/
    public static final int JDO_1_0_0_LOCATIONS = 2;

    /** MetaData files will be searched in all locations defined in JDO 1.0.1 **/
    public static final int JDO_1_0_1_LOCATIONS = 3;

    /** Definition of which locations we accept for MetaData files. */
    protected int locationDefinition = ALL_JDO_LOCATIONS;

    /** Map of ClassMetaData from ORM files, keyed by the class name. */
    protected Map ormClassMetaDataByClass = new HashMap();

    /**
     * Map of ClassMetaData, keyed by the interface class name (for "persistent-interface"s).
     * Keyed by the persistent-interface name.
     */
    protected Map classMetaDataByInterface = new HashMap();

    /**
     * Constructor.
     * @param ctxt ObjectManagerFactory Context that this metadata manager operates in
     */
    public JDOMetaDataManager(OMFContext ctxt)
    {
        super(ctxt);

        locationDefinition = ALL_JDO_LOCATIONS;

        // Log the current configuration
        if (JPOXLogger.METADATA.isDebugEnabled())
        {
            logConfiguration();
        }
    }

    /**
     * Convenience accessor for the mapping name.
     * @return ORM mapping name
     */
    protected String getORMMappingName()
    {
        String mappingName = omfContext.getPersistenceConfiguration().getStringProperty("org.jpox.Mapping");
        return (StringUtils.isWhitespace(mappingName) ? null : mappingName);
    }

    /**
     * Convenience accessor for the JDO file suffix.
     * @return JDO file suffix
     */
    public String getJDOFileSuffix()
    {
        String suffix = omfContext.getPersistenceConfiguration().getStringProperty("org.jpox.metadata.jdoFileExtension");
        return (StringUtils.isWhitespace(suffix) ? "jdo" : suffix);
    }

    /**
     * Convenience accessor for the ORM file suffix.
     * @return ORM file suffix
     */
    public String getORMFileSuffix()
    {
        String suffix = omfContext.getPersistenceConfiguration().getStringProperty("org.jpox.metadata.ormFileExtension");
        return (StringUtils.isWhitespace(suffix) ? "orm" : suffix);
    }

    /**
     * Convenience accessor for the JDOQuery file suffix.
     * @return JDOQuery file suffix
     */
    public String getJDOQueryFileSuffix()
    {
        String suffix = omfContext.getPersistenceConfiguration().getStringProperty("org.jpox.metadata.jdoqueryFileExtension");
        return (StringUtils.isWhitespace(suffix) ? "jdoquery" : suffix);
    }

    /**
     * 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";
        }

        if (JPOXLogger.METADATA.isDebugEnabled())
        {
            if (supportsORM)
            {
                String mappingName = getORMMappingName();
                String jdoSuffix = getJDOFileSuffix();
                String ormSuffix = getORMFileSuffix();
                String jdoquerySuffix = getJDOQueryFileSuffix();
                JPOXLogger.METADATA.debug("MetaDataManager : " +
                    " Input=(" + inputTypes + ")" +
                    " XML-Validation=" + validateMetaData +
                    " XML-Files=(persistence=*." + jdoSuffix + ", ORM=*." + ormSuffix + ", query=*." + jdoquerySuffix + ")" +
                    (mappingName != null ? (" ORM-name=" + mappingName) : ""));
            }
            else
            {
                String jdoSuffix = getJDOFileSuffix();
                String jdoquerySuffix = getJDOQueryFileSuffix();
                JPOXLogger.METADATA.debug("MetaDataManager : " +
                    " Input=(" + inputTypes + ")" +
                    " XML-Validation=" + validateMetaData +
                    " XML-Files=(persistence=*." + jdoSuffix + ", query=*." + jdoquerySuffix + ")");
            }
        }
    }

    /**
     * Clear resources
     */
    public void close()
    {
        super.close();
        ormClassMetaDataByClass.clear();
        ormClassMetaDataByClass = null;
    }

    /**
     * Utility to parse a file, using the "jdo" MetaData handler.
     * @param fileURL URL of the file
     * @return The FileMetaData for this file
     */
    protected FileMetaData parseFile(URL fileURL)
    {
        if (metaDataParser == null)
        {
            metaDataParser = new MetaDataParser(this, validateMetaData);
        }
        return (FileMetaData)metaDataParser.parseMetaDataURL(fileURL, "jdo");
    }

    /**
     * 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 void registerFile(String fileURLString, FileMetaData filemd, ClassLoaderResolver clr)
    {
        if (fileURLString == null)
        {
            // Null file
            return;
        }
        if (fileMetaDataByURLString.get(fileURLString) != null)
        {
            // Already registered!
            return;
        }

        fileMetaDataByURLString.put(fileURLString, filemd);

        registerQueriesForFile(filemd);
        registerFetchPlansForFile(filemd);
        registerSequencesForFile(filemd);
        registerTableGeneratorsForFile(filemd);

        // Register the classes and interfaces for later use
        if (filemd.getType() != FileMetaData.JDOQUERY_FILE)
        {
            for (int i = 0; i < filemd.getNoOfPackages(); i++)
            {
                PackageMetaData pmd = filemd.getPackage(i);

                // Register all classes into the respective lookup maps
                for (int j = 0; j < pmd.getNoOfClasses(); j++)
                {
                    ClassMetaData cmd = pmd.getClass(j);
                    if (filemd.getType() == FileMetaData.JDO_FILE || filemd.getType() == FileMetaData.ANNOTATIONS)
                    {
                        registerMetaDataForClass(cmd.getFullClassName(), cmd);
                    }
                    else if (filemd.getType() == FileMetaData.ORM_FILE)
                    {
                        ormClassMetaDataByClass.put(cmd.getFullClassName(), cmd);
                    }
                    if (cmd.getEntityName() != null)
                    {
                        // Register the ClassMetaData against the "entity name"
                        classMetaDataByEntityName.put(cmd.getEntityName(), cmd);
                    }
                }

                // Register all interfaces into the respective lookup maps
                for (int j = 0; j < pmd.getNoOfInterfaces(); j++)
                {
                    InterfaceMetaData intfmd = pmd.getInterface(j);
                    if (filemd.getType() == FileMetaData.JDO_FILE || filemd.getType() == FileMetaData.ANNOTATIONS)
                    {
                        registerMetaDataForClass(intfmd.getFullClassName(), intfmd);
                    }
                    else if (filemd.getType() == FileMetaData.ORM_FILE)
                    {
                        ormClassMetaDataByClass.put(intfmd.getFullClassName(), intfmd);
                    }
                }
            }
        }
    }

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

    /**
     * Internal method for accessing the MetaData for a class.
     * The MetaData returned can be uninitialised.
     * Runs through the following process
     * <OL>
     * <LI>Checks if the class is known not to have metata</LI>
     * <LI>Check if we have metadata for the class in one of the files that has
     * been parsed.</LI>
     * <LI>If we have metadata, check that it is initialised</LI>
     * <LI>If we don't have metadata, find the file for this class.</LI>
     * <LI>If we cant find a file for it, add it to the list of classes known
     * to have no metadata</LI>
     * </OL>
     * @param c The class to find MetaData for
     * @return The ClassMetaData for this class (or null if not found)
     **/
    public synchronized AbstractClassMetaData getMetaDataForClassInternal(Class c, ClassLoaderResolver clr)
    {
        String className = c.getName();
        // If we know that this class/interface has no MetaData/annotations don't bother searching
        if (isClassWithoutPersistenceInfo(className))
        {
            return null;
        }

        // Check if we have the MetaData already
        AbstractClassMetaData the_md = (AbstractClassMetaData) classMetaDataByClass.get(className);
        if (the_md != null)
        {
            return the_md;
        }

        // No loaded MetaData so search valid location for a file for this class and load all in the process
        FileMetaData filemd = loadMetaDataForClass(c, clr, null, getJDOFileSuffix(), true);
        if (filemd != null)
        {
            // Class has had its metadata loaded
            utilisedFileMetaData.add(filemd);

            // If not MetaData complete will also merge in annotations at populate stage

            // Retrieve the MetaData for the requested class
            the_md = (AbstractClassMetaData)classMetaDataByClass.get(className);

            return the_md;
        }

        // No MetaData so check for annotations
        FileMetaData annFilemd = loadAnnotationsForClass(c, clr, true, true);
        if (annFilemd != null)
        {
            // No MetaData but annotations present so use that
            if (c.isInterface())
            {
                return annFilemd.getPackage(0).getInterface(0);
            }
            else
            {
                return annFilemd.getPackage(0).getClass(0);
            }
        }

        // Not found, so add to known classes/interfaces without MetaData
        if (JPOXLogger.METADATA.isDebugEnabled())
        {
            JPOXLogger.METADATA.debug(LOCALISER.msg("044043", className));
        }
        classesWithoutPersistenceInfo.add(className);

        return null;
    }

    /**
     * Accessor for the MetaData for a named query for a class.
     * If the query is not found, will check all valid JDO file locations and try to load it.
     * @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)
    {
        QueryMetaData qmd = super.getMetaDataForQuery(cls, clr, queryName);
        if (qmd != null)
        {
            return qmd;
        }

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

        // No query found, so try to load one from a valid JDO location
        if (cls != null)
        {
            // Query is scoped to a candidate class, so load the class as necessary
            AbstractClassMetaData cmd = getMetaDataForClass(cls, clr);
            if (cmd == null)
            {
                // No metadata for this class so no chance of finding the query for it
                return null;
            }

            Object obj = queryMetaDataByName.get(query_key);
            if (obj != null)
            {
                return (QueryMetaData)obj;
            }

            // Query not stored in JDO/ORM files so try JDOQUERY
            List locations = new ArrayList();
            locations.addAll(getValidMetaDataLocationsForClass(getJDOQueryFileSuffix(), null, cls.getName()));

            for (int i=0; i<locations.size(); i++)
            {
                String location = (String) locations.get(i);
                // Process all resources for this location
                Enumeration resources;
                try
                {
                    resources = clr.getResources(location, cls != null ? cls.getClassLoader() : null);
                }
                catch (IOException e)
                {
                    throw new JPOXException("Error loading resource", e).setFatal();
                }
                while (resources.hasMoreElements())
                {
                    URL fileURL = (URL) resources.nextElement();
                    if (fileMetaDataByURLString.get(fileURL.toString()) == null)
                    {
                        // File hasn't been loaded so load it
                        FileMetaData filemd = parseFile(fileURL);
                        filemd.setType(FileMetaData.JDOQUERY_FILE); // TODO Remove this since set in the parser at <jdoquery>
                        registerFile(fileURL.toString(), filemd, clr);

                        // Populate all classes in this file we've just parsed
                        // TODO Populate the classes found in this file
                    }
                }
                cmd = getMetaDataForClass(cls, clr);

                qmd = (QueryMetaData)queryMetaDataByName.get(query_key);
                if (qmd != null)
                {
                    if (JPOXLogger.METADATA.isDebugEnabled())
                    {
                        JPOXLogger.METADATA.debug(LOCALISER.msg("044053",query_key,location));
                    }
                    return qmd;
                }
                if (JPOXLogger.METADATA.isDebugEnabled())
                {
                    JPOXLogger.METADATA.debug(LOCALISER.msg("044050",query_key,location));
                }
            }
            return null;
        }

        // Query isn't scoped to a candidate class, so search the valid package-independent locations
        List locations = new ArrayList();
        locations.addAll(getValidMetaDataLocationsForItem(getJDOFileSuffix(), null, null, false));
        locations.addAll(getValidMetaDataLocationsForItem(getORMFileSuffix(), getORMMappingName(), null, false));
        locations.addAll(getValidMetaDataLocationsForItem(getJDOQueryFileSuffix(), null, null, false));

        for (int i=0; i<locations.size(); i++)
        {
            String location = (String) locations.get(i);
            // Process all resources for this location
            Enumeration resources;
            try
            {
                resources = clr.getResources(location, cls != null ? cls.getClassLoader() : null);
            }
            catch (IOException e)
            {
                throw new JPOXException("Error loading resources", e).setFatal();
            }
            while (resources.hasMoreElements())
            {
                URL fileURL = (URL) resources.nextElement();
                if (fileMetaDataByURLString.get(fileURL.toString()) == null)
                {
                    // File hasn't been loaded so load it
                    FileMetaData filemd = parseFile(fileURL);
                    registerFile(fileURL.toString(), filemd, clr);

                    // Populate all classes in this file we've just parsed
                    // TODO Populate the classes found in this file
                }
            }

            qmd = (QueryMetaData)queryMetaDataByName.get(query_key);
            if (qmd != null)
            {
                if (JPOXLogger.METADATA.isDebugEnabled())
                {
                    JPOXLogger.METADATA.debug(LOCALISER.msg("044053",query_key,location));
                }
                return qmd;
            }
            if (JPOXLogger.METADATA.isDebugEnabled())
            {
                JPOXLogger.METADATA.debug(LOCALISER.msg("044050",query_key,location));
            }
        }
        return null;
    }

    /**
     * Accessor for the MetaData for a Sequence in a package.
     * If the sequence is not yet known will search the valid locations for the passed name.
     * @param clr the ClassLoaderResolver
     * @param packageSequenceName Fully qualified name of the sequence (inc package name)
     * @return The SequenceMetaData for this named sequence
     **/
    public SequenceMetaData getMetaDataForSequence(ClassLoaderResolver clr, String packageSequenceName)
    {
        SequenceMetaData seqmd = super.getMetaDataForSequence(clr, packageSequenceName);
        if (seqmd != null)
        {
            return seqmd;
        }

        // MetaData not found so maybe just not yet loaded
        String packageName = packageSequenceName;
        if (packageSequenceName.lastIndexOf('.') >= 0)
        {
            packageName = packageSequenceName.substring(0, packageSequenceName.lastIndexOf('.'));
        }

        // Search valid JDO file locations ("jdo" and "orm" files for the specified package)
        List locations = new ArrayList();
        locations.addAll(getValidMetaDataLocationsForItem(getJDOFileSuffix(), null, packageName, false));
        locations.addAll(getValidMetaDataLocationsForItem(getORMFileSuffix(), getORMMappingName(), packageName, false));

        for (int i=0; i<locations.size(); i++)
        {
            String location = (String) locations.get(i);
            // Process all resources for this location
            Enumeration resources;
            try
            {
                resources = clr.getResources(location, null);
            }
            catch (IOException e)
            {
                throw new JPOXException("Error loading resource", e).setFatal();
            }
            while (resources.hasMoreElements())
            {
                URL fileURL = (URL) resources.nextElement();
                if (fileMetaDataByURLString.get(fileURL.toString()) == null)
                {
                    // File hasn't been loaded so load it
                    FileMetaData filemd = parseFile(fileURL);
                    registerFile(fileURL.toString(), filemd, clr);

                    // Populate all classes in this file we've just parsed
                    // TODO Populate the classes found in this file
                }
            }

            seqmd = (SequenceMetaData)sequenceMetaDataByPackageSequence.get(packageSequenceName);
            if (seqmd != null)
            {
                if (JPOXLogger.METADATA.isDebugEnabled())
                {
                    JPOXLogger.METADATA.debug(LOCALISER.msg("044053", packageSequenceName, location));
                }
                return seqmd;
            }
            if (JPOXLogger.METADATA.isDebugEnabled())
            {
                JPOXLogger.METADATA.debug(LOCALISER.msg("044051", packageSequenceName, location));
            }
        }
        return null;
    }

    /**
     * Load up and add the O/R mapping info for the specified class to the stored JDO ClassMetaData.
     * @param c The class
     * @param clr the ClassLoaderResolver
     */
    public void addORMDataToClass(Class c, ClassLoaderResolver clr)
    {
        if (enhancing)
        {
            // We don't need ORM data when enhancing
            return;
        }
        if (!supportsORM)
        {
            // StoreManager doesnt "map" to the datastore so don't use ORM info
            return;
        }

        // Get the JDO MetaData for this class/interface
        AbstractClassMetaData cmd = (AbstractClassMetaData)classMetaDataByClass.get(c.getName());

        // See if we already have a file registered with the ORM metadata for this class
        AbstractClassMetaData ormCmd = (AbstractClassMetaData)ormClassMetaDataByClass.get(c.getName());
        if (ormCmd != null)
        {
            // Merge the ORM class into the JDO class
            MetaDataMerger.mergeClassORMData(cmd, ormCmd);

            // Remove it from the map since no longer needed
            ormClassMetaDataByClass.remove(c.getName());

            return;
        }

        // No ORM loaded for this class, so find if there is any ORM metadata available
        FileMetaData filemdORM = loadMetaDataForClass(c, clr, getORMMappingName(), getORMFileSuffix(), false);
        if (filemdORM != null)
        {
            // The ORM file has now been registered, so find the class and merge it into the JDO definition
            ormCmd = (AbstractClassMetaData)ormClassMetaDataByClass.get(c.getName());
            if (ormCmd != null)
            {
                // Merge the ORM file into the JDO file
                MetaDataMerger.mergeFileORMData((FileMetaData)cmd.getPackageMetaData().getParent(),
                    (FileMetaData)ormCmd.getPackageMetaData().getParent());

                // Merge the ORM class into the JDO class
                MetaDataMerger.mergeClassORMData(cmd, ormCmd);

                // Remove it from the map since no longer needed
                ormClassMetaDataByClass.remove(c.getName());
            }
        }
    }

    /**
     * Method to find the Meta-Data file for a specified class.
     * Checks the locations one-by-one, and checks for existence of the
     * specified class in the file. If a valid file is found it is loaded no matter
     * if the file contains the actual class. When a file is found containing the class
     * the process stops and the FileMetaData for that file (containing the class) returned.
     * <P>
     * Allows 2 variations on the naming above. The first is a modifier which
     * caters for a JDO 2.0 requirement whereby the user can specify a modifier
     * such as "mysql", which would mean that this should search for filenames
     * "package-mysql.jdo". The second variation is the suffix of the file.
     * This is "jdo" by default, but JDO 2.0 has situations where "orm", or
     * "jdoquery" are required as a suffix.
     * </P>
     * @param pc_class The class/interface to retrieve the Meta-Data file for
     * @param clr the ClassLoaderResolver
     * @param metadata_file_modifier Any modifier for the filename
     * @param metadata_file_extension File extension of MetaData files (e.g "jdo")
     * @param populate Whether to populate any loaded MetaData classes
     * @return FileMetaData for the file containing the class
     **/
    protected FileMetaData loadMetaDataForClass(Class pc_class,
                                                ClassLoaderResolver clr,
                                                String metadata_file_modifier,
                                                String metadata_file_extension,
                                                boolean populate)
    {
        // MetaData file locations
        List validLocations = getValidMetaDataLocationsForClass(metadata_file_extension, metadata_file_modifier, pc_class.getName());
        Iterator locationsIter = validLocations.iterator();
        while (locationsIter.hasNext())
        {
            String location = (String)locationsIter.next();
            Enumeration resources;
            try
            {
                resources = clr.getResources(location, pc_class.getClassLoader());
            }
            catch (IOException e)
            {
                throw new JPOXException("Error loading resource", e).setFatal();
            }
            if (!resources.hasMoreElements() && JPOXLogger.METADATA.isDebugEnabled())
            {
                JPOXLogger.METADATA.debug(LOCALISER.msg("044049",
                    metadata_file_extension, pc_class.getName(), location));
            }
            while (resources.hasMoreElements())
            {
                URL url = (URL) resources.nextElement();
                if (url != null)
                {
                    // File exists (valid URL), so check if we already have this file registered
                    FileMetaData filemd = (FileMetaData)fileMetaDataByURLString.get(url.toString());
                    if (filemd == null)
                    {
                        // Not registered so load the file from the URL
                        filemd = parseFile(url);
                        registerFile(url.toString(), filemd, clr);

                        if (populate)
                        {
                            // Populate all classes in this file we've just parsed
                            populateFileMetaData(filemd, clr, pc_class.getClassLoader());
                        }
                    }

                    if ((filemd.getType() == FileMetaData.JDO_FILE && classMetaDataByClass.get(pc_class.getName()) != null) ||
                        (filemd.getType() == FileMetaData.ORM_FILE && ormClassMetaDataByClass.get(pc_class.getName()) != null))
                    {
                        // We now have the class, so it must have been in this file
                        if (JPOXLogger.METADATA.isDebugEnabled())
                        {
                            JPOXLogger.METADATA.debug(LOCALISER.msg("044052", metadata_file_extension, pc_class.getName(), url));
                        }
                        return filemd;
                    }
                }
            }
        }

        if (JPOXLogger.METADATA.isDebugEnabled())
        {
            JPOXLogger.METADATA.debug(LOCALISER.msg("044048", metadata_file_extension, pc_class.getName()));
        }
        return null;
    }

    /**
     * Method to return the valid metadata locations to contain a particular package.
     * @param fileExtension File extension (e.g "jdo")
     * @param fileModifier Any modifier (for use when using ORM files package-mysql.orm, this is the "mysql" part)
     * @param packageName The package name to look for
     * @return The list of valid locations
     */
    public List getValidMetaDataLocationsForPackage(String fileExtension, String fileModifier, String packageName)
    {
        return getValidMetaDataLocationsForItem(fileExtension, fileModifier, packageName, false);
    }

    /**
     * Method to return the valid metadata locations to contain a particular class.
     * @param fileExtension File extension (e.g "jdo")
     * @param fileModifier Any modifier (for use when using ORM files package-mysql.orm, this is the "mysql" part)
     * @param className The class name to look for
     * @return The list of valid locations
     */
    public List getValidMetaDataLocationsForClass(String fileExtension, String fileModifier, String className)
    {
        return getValidMetaDataLocationsForItem(fileExtension, fileModifier, className, true);
    }

    // Parameters used in the definition of MetaData file location
    private static final char CLASS_SEPARATOR = '.';
    private static final char PATH_SEPARATOR = '/';
    private static final char EXTENSION_SEPARATOR = '.';
    private static final String METADATA_PACKAGE = "package";
    private static final String METADATA_LOCATION_METAINF = "/META-INF/" + METADATA_PACKAGE;
    private static final String METADATA_LOCATION_WEBINF = "/WEB-INF/" + METADATA_PACKAGE;

    /**
     * Method to return the valid metadata locations to contain a particular item. The
     * "item" can be a package or a class. Will look in the locations appropriate for the
     * setting of "locationDefintion".
     * @param fileExtension File extension (e.g "jdo") accepts comma separated list
     * @param fileModifier Any modifier (for use when using ORM files package-mysql.orm, this is the "mysql" part)
     * @param itemName The name of the item (package or class)
     * @param isClass Whether this is a class
     * @return The list of valid locations
     */
    List getValidMetaDataLocationsForItem(String fileExtension, String fileModifier, String itemName, boolean isClass)
    {
        // Build up a list of valid locations
        List locations = new ArrayList();

        if (fileExtension == null)
        {
            fileExtension = "jdo";
        }
        StringTokenizer tokens = new StringTokenizer(fileExtension,",");
        while( tokens.hasMoreTokens() )
        {
            locations.addAll(getValidMetaDataLocationsForSingleExtension(tokens.nextToken(),fileModifier,itemName,isClass));
        }
        return locations;
    }
    /**
     * Method to return the valid metadata locations to contain a particular item. The
     * "item" can be a package or a class. Will look in the locations appropriate for the
     * setting of "locationDefintion".
     * @param fileExtension File extension (e.g "jdo")
     * @param fileModifier Any modifier (for use when using ORM files package-mysql.orm, this is the "mysql" part)
     * @param itemName The name of the item (package or class)
     * @param isClass Whether this is a class
     * @return The list of valid locations
     */
    private List getValidMetaDataLocationsForSingleExtension(String fileExtension, String fileModifier, String itemName, boolean isClass)
    {
        // Build up a list of valid locations
        List locations = new ArrayList();

        String suffix = null;
        if (fileExtension == null)
        {
            fileExtension = "jdo";
        }
        if (fileModifier != null)
        {
            // This will be something like "-mysql.orm" (suffix for ORM files)
            suffix = "-" + fileModifier + EXTENSION_SEPARATOR + fileExtension;
        }
        else
        {
            suffix = EXTENSION_SEPARATOR + fileExtension;
        }

        if (locationDefinition == ALL_JDO_LOCATIONS || locationDefinition == JDO_1_0_1_LOCATIONS)
        {
            locations.add((METADATA_LOCATION_METAINF + suffix)); // "/META-INF/package.jdo" (JDO 1.0.1)
            locations.add((METADATA_LOCATION_WEBINF + suffix)); // "/WEB-INF/package.jdo" (JDO 1.0.1)
            locations.add(PATH_SEPARATOR + METADATA_PACKAGE + suffix); // "/package.jdo" (JDO 1.0.1)
        }
        if (itemName != null && itemName.length() > 0)
        {
            int separatorPosition = itemName.indexOf('.');
            if (separatorPosition < 0)
            {
                if (locationDefinition == ALL_JDO_LOCATIONS || locationDefinition == JDO_1_0_1_LOCATIONS)
                {
                    // "/com/package.jdo" (JDO 1.0.1)
                    locations.add(PATH_SEPARATOR + itemName + PATH_SEPARATOR + METADATA_PACKAGE + suffix);
                }
                if (locationDefinition == ALL_JDO_LOCATIONS || locationDefinition == JDO_1_0_0_LOCATIONS)
                {
                    // "/com.jdo" (JDO 1.0.0)
                    locations.add(PATH_SEPARATOR + itemName + suffix);
                }
            }
            else
            {
                while (separatorPosition >= 0)
                {
                    String name = itemName.substring(0, separatorPosition);

                    if (locationDefinition == ALL_JDO_LOCATIONS || locationDefinition == JDO_1_0_1_LOCATIONS)
                    {
                        // "/com/xyz/package.jdo" (JDO 1.0.1)
                        locations.add(PATH_SEPARATOR + name.replace(CLASS_SEPARATOR, PATH_SEPARATOR) + PATH_SEPARATOR + METADATA_PACKAGE + suffix);
                    }
                    if (locationDefinition == ALL_JDO_LOCATIONS || locationDefinition == JDO_1_0_0_LOCATIONS)
                    {
                        // "/com/xyz.jdo" (JDO 1.0.0)
                        locations.add(PATH_SEPARATOR + name.replace(CLASS_SEPARATOR, PATH_SEPARATOR) + suffix);
                    }

                    separatorPosition = itemName.indexOf('.', separatorPosition+1);
                    if (separatorPosition < 0)
                    {
                        if (!isClass)
                        {
                            if (locationDefinition == ALL_JDO_LOCATIONS || locationDefinition == JDO_1_0_1_LOCATIONS)
                            {
                                // "/com/xyz/uvw/package.jdo" (JDO 1.0.1)
                                locations.add(PATH_SEPARATOR + itemName.replace(CLASS_SEPARATOR, PATH_SEPARATOR) + PATH_SEPARATOR + METADATA_PACKAGE + suffix);
                            }
                        }

                        if (locationDefinition == ALL_JDO_LOCATIONS || locationDefinition == JDO_1_0_0_LOCATIONS)
                        {
                            // "/com/xyz/uvw.jdo" (JDO 1.0.0)
                            locations.add(PATH_SEPARATOR + itemName.replace(CLASS_SEPARATOR, PATH_SEPARATOR) + suffix);
                        }
                    }
                }
            }
        }

        return locations;
    }

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

    /**
     * Main accessor for the MetaData for a "persistent-interface".
     * All MetaData returned from this method will be initialised and ready for full use.
     * @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)
    {
        if (c == null || !c.isInterface())
        {
            return null;
        }

        InterfaceMetaData imd = (InterfaceMetaData)getMetaDataForClassInternal(c, clr);
        if (imd != null)
        {
            // Make sure that anything returned is initialised
            if (!imd.isPopulated() && !imd.isInitialised())
            {
                // Initialise the interface in question
                imd.populate(clr,c.getClassLoader());
            }
            // Make sure that anything returned is initialised
            if (!imd.isInitialised())
            {
                // Initialise the interface in question
                imd.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());
                }
            }
        }

        utilisedFileMetaData.clear();
        return imd;
    }

    /**
     * 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)
    {
        // Find if this class has <interface> metadata
        AbstractClassMetaData acmd = (AbstractClassMetaData)classMetaDataByClass.get(name);
        return (acmd != null && acmd instanceof InterfaceMetaData);
    }

    /**
     * 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)
    {
        ClassMetaData cmd = (ClassMetaData)classMetaDataByInterface.get(interfaceName);
        return (cmd != null && cmd.getFullClassName().equals(implName));
    }

    /**
     * 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)
    {
        ClassMetaData cmd = (ClassMetaData)classMetaDataByClass.get(implName);
        return (cmd != null && cmd.isImplementationOfPersistentDefinition());
    }

    /**
     * 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)
    {
        ClassMetaData cmd = (ClassMetaData)classMetaDataByInterface.get(interfaceName);
        return (cmd != null ? cmd.getFullClassName() : 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)
    {
        return (ClassMetaData)classMetaDataByInterface.get(interfaceName);
    }

    /**
     * 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)
    {
        // Create ClassMetaData for the implementation
        ClassMetaData cmd = new ClassMetaData(imd, implClass.getName(), true);
       
        cmd.addImplements(new ImplementsMetaData(cmd,imd.getFullClassName()));

        // Register the ClassMetaData for the implementation
        registerMetaDataForClass(cmd.getFullClassName(), cmd);

        // Register the metadata for the implementation against this persistent interface
        classMetaDataByInterface.put(imd.getFullClassName(), cmd);

        initialiseClassMetaData(cmd, implClass, clr);

        // Deregister the metadata for the implementation from those "not found"
        if (JPOXLogger.METADATA.isDebugEnabled())
        {
            JPOXLogger.METADATA.debug(LOCALISER.msg("044044",implClass.getName()));
        }
        classesWithoutPersistenceInfo.remove(implClass.getName());
    }

    /**
     * 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)
    {
        ClassMetaData implCmd = new ClassMetaData(cmd, implClass.getName());

        // Register the ClassMetaData for the implementation
        registerMetaDataForClass(implCmd.getFullClassName(), implCmd);
        initialiseClassMetaData(implCmd, implClass, clr);

        // Deregister the metadata for the implementation from those "not found"
        if (JPOXLogger.METADATA.isDebugEnabled())
        {
            JPOXLogger.METADATA.debug(LOCALISER.msg("044044", implClass.getName()));
        }
        classesWithoutPersistenceInfo.remove(implClass.getName());
    }
}
TOP

Related Classes of org.jpox.jdo.metadata.JDOMetaDataManager

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.