/*******************************************************************************
 * Copyright (c) 1998, 2014 Oracle and/or its affiliates, IBM Corporation. All rights reserved.
 * This program and the accompanying materials are made available under the 
 * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 
 * which accompanies this distribution. 
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at 
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * Contributors:
 *     Oracle - initial API and implementation from Oracle TopLink
 *     05/16/2008-1.0M8 Guy Pelletier 
 *       - 218084: Implement metadata merging functionality between mapping files
 *     05/23/2008-1.0M8 Guy Pelletier 
 *       - 211330: Add attributes-complete support to the EclipseLink-ORM.XML Schema
 *     12/10/2008-1.1 Michael O'Brien 
 *       - 257606: Add orm.xml schema validation true/(false) flag support in persistence.xml
 *     01/28/2009-2.0 Guy Pelletier 
 *       - 248293: JPA 2.0 Element Collections (part 1)
 *     03/27/2009-2.0 Guy Pelletier 
 *       - 241413: JPA 2.0 Add EclipseLink support for Map type attributes
 *     03/08/2010-2.1 Guy Pelletier 
 *       - 303632: Add attribute-type for mapping attributes to EclipseLink-ORM
 *     05/14/2010-2.1 Guy Pelletier 
 *       - 253083: Add support for dynamic persistence using ORM.xml/eclipselink-orm.xml
 *     04/01/2011-2.3 Guy Pelletier 
 *       - 337323: Multi-tenant with shared schema support (part 2)
 *     09/20/2011-2.3.1 Guy Pelletier 
 *       - 357476: Change caching default to ISOLATED for multitenant's using a shared EMF.
 *     06/20/2012-2.5 Guy Pelletier 
 *       - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls
 *     10/09/2012-2.5 Guy Pelletier 
 *       - 374688: JPA 2.1 Converter support
 *     08/18/2014-2.5 Jody Grassel (IBM Corporation)
 *       - 440802: xml-mapping-metadata-complete does not exclude @Entity annotated entities
 ******************************************************************************/  
package org.eclipse.persistence.internal.jpa.metadata;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
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 javax.persistence.spi.PersistenceUnitInfo;

import org.eclipse.persistence.config.DescriptorCustomizer;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.exceptions.PersistenceUnitLoadingException;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl;
import org.eclipse.persistence.internal.jpa.deployment.PersistenceUnitProcessor;
import org.eclipse.persistence.internal.jpa.deployment.PersistenceUnitProcessor.Mode;
import org.eclipse.persistence.internal.jpa.metadata.accessors.classes.ClassAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.classes.ConverterAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.classes.EmbeddableAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.classes.EntityAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAsmFactory;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataClass;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataFactory;
import org.eclipse.persistence.internal.jpa.metadata.converters.StructConverterMetadata;
import org.eclipse.persistence.internal.jpa.metadata.xml.XMLEntityMappings;
import org.eclipse.persistence.internal.jpa.metadata.xml.XMLEntityMappingsReader;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.jpa.Archive;
import org.eclipse.persistence.jpa.metadata.MetadataSource;
import org.eclipse.persistence.logging.AbstractSessionLog;
import org.eclipse.persistence.logging.SessionLog;

/**
 * INTERNAL:
 * The object/relational metadata processor for the EJB3.0 specification. 
 * 
 * @author Guy Pelletier
 * @since TopLink EJB 3.0 Reference Implementation
 */
public class MetadataProcessor {
    protected ClassLoader m_loader;
    protected MetadataFactory m_factory;
    protected MetadataProject m_project;
    protected AbstractSession m_session;
    protected Map m_predeployProperties;
    protected MetadataProcessor m_compositeProcessor;
    protected Set<MetadataProcessor> m_compositeMemberProcessors;
    protected MetadataSource m_metadataSource;

    /**
     * INTERNAL:
     * Empty processor to be used as a composite processor.
     */
    public MetadataProcessor() {}
    
    /**
     * INTERNAL:
     * Called from EntityManagerSetupImpl. The 'real' EJB 3.0 processing
     * that includes XML and annotations.
     */
    public MetadataProcessor(PersistenceUnitInfo puInfo, AbstractSession session, ClassLoader loader, boolean weaveLazy, boolean weaveEager, boolean weaveFetchGroups, boolean multitenantSharedEmf, boolean multitenantSharedCache, Map predeployProperties, MetadataProcessor compositeProcessor) {
        m_loader = loader;
        m_session = session;
        m_project = new MetadataProject(puInfo, session, weaveLazy, weaveEager, weaveFetchGroups, multitenantSharedEmf, multitenantSharedCache);
        m_predeployProperties = predeployProperties;
        m_compositeProcessor = compositeProcessor;
        if (m_compositeProcessor != null) {
            m_compositeProcessor.addCompositeMemberProcessor(this);
            m_project.setCompositeProcessor(m_compositeProcessor);
        }
    }
    
    /**
     * INTERNAL:
     * Add containedProcessor to compositeProcessor.
     */
    public void addCompositeMemberProcessor(MetadataProcessor compositeMemberProcessor) {
        if (m_compositeMemberProcessors == null) {
            m_compositeMemberProcessors = new HashSet();
        }
        m_compositeMemberProcessors.add(compositeMemberProcessor);
    }
    
    /**
     * INTERNAL: 
     * Method to place EntityListener's on the descriptors from the given 
     * session. This call is made from the EntityManagerSetup deploy call.
     */
    public void addEntityListeners() {
        // Process the listeners for all the class accessors, but before
        // doing so, update the accessors associated class since the loader 
        // should have changed changed.
        for (EntityAccessor accessor : m_project.getEntityAccessors()) { 
            accessor.setJavaClass(accessor.getDescriptor().getJavaClass());
            accessor.processListeners(m_loader);
        }
    }
    
    /**
     * INTERNAL:
     * Method to place NamedQueries and NamedNativeQueries on the given session. 
     * This call is made from the EntityManagerSetup deploy call.
     */
    public void addNamedQueries() {
        m_project.processQueries();
    }
    
    /**
     * INTERNAL:
     * During EntityManagerSetup deploy, using the real class loader we must
     * create our dynamic classes.
     */
    public void createDynamicClasses() {
        m_project.createDynamicClasses(m_loader);
    }
    
    public void createRestInterfaces(){
        m_project.createRestInterfaces(m_loader);
    }
        
    /**
     * INTERNAL:
     * Return compositeProcessor.
     */
    public MetadataProcessor getCompositeProcessor() {
        return m_compositeProcessor;
    }
    
    /**
     * INTERNAL:
     */
    public MetadataFactory getMetadataFactory() {
        return m_factory;
    }

    /**
     * INTERNAL:
     */
    public MetadataSource getMetadataSource(){
        return m_metadataSource;
    }

    /**
     * INTERNAL:
     * Returns projects owned by compositeProcessor minus the passed project.
     */
    public Set<MetadataProject> getPearProjects(MetadataProject project) {
        Set<MetadataProject> pearProjects = new HashSet();
        if (m_compositeMemberProcessors != null) {
            for(MetadataProcessor processor : m_compositeMemberProcessors) {
                MetadataProject pearProject = processor.getProject();
                if(pearProject != project) {
                    pearProjects.add(pearProject);
                }
            }
        }
        return pearProjects;
    }
    
    /**
     * INTERNAL:
     * Return a set of class names for each entity, embeddable and mapped 
     * superclass found in the mapping files to be processed by the 
     * MetadataProcessor.
     */
    public Set<String> getPersistenceUnitClassSetFromMappingFiles() {
        HashSet<String> classSet = new HashSet<String>();
        
        for (XMLEntityMappings entityMappings : m_project.getEntityMappings()) {
            for (ClassAccessor entity : entityMappings.getEntities()) {
                classSet.add(entityMappings.getPackageQualifiedClassName(entity.getClassName()));
            }
            
            for (ClassAccessor embeddable : entityMappings.getEmbeddables()) {
                classSet.add(entityMappings.getPackageQualifiedClassName(embeddable.getClassName()));
            }
            
            for (ClassAccessor mappedSuperclass : entityMappings.getMappedSuperclasses()) {
                classSet.add(entityMappings.getPackageQualifiedClassName(mappedSuperclass.getClassName()));
            }
        }
        
        return classSet;
    }

    /**
     * INTERNAL:
     */
    public MetadataProject getProject() {
        return m_project;
    }
    
    /**
     * INTERNAL:
     * Adds a list of StructConverter string names that were defined in the
     * metadata of this project to the native EclipseLink project.
     * 
     * These StructConverters can be added to the Project to be processed later 
     */
    public void addStructConverterNames() {
        List<String> structConverters = new ArrayList<String>();
        for (StructConverterMetadata converter: m_project.getStructConverters()) {
            structConverters.add(converter.getConverterClassName());
        }
        if (!structConverters.isEmpty()) {
            m_session.getProject().setStructConverters(structConverters);
        }
    }
     
    /**
     * INTERNAL:
     * Handle an exception that occurred while processing ORM xml.
     */
    protected void handleORMException(RuntimeException e, String mappingFile, boolean throwException){
        if (m_session == null) {
            // Metadata processor is mainly used with a session. Java SE 
            // bootstrapping uses some functions such as ORM processing without
            // a session. In these cases, it is impossible to get the session 
            // to properly handle the exception. As a result we log an error. 
            // The same code will be called later in the bootstrapping code 
            // and the error will be handled then.
            AbstractSessionLog.getLog().log(SessionLog.WARNING, SessionLog.METADATA, EntityManagerSetupImpl.ERROR_LOADING_XML_FILE, new Object[] {mappingFile, e});
        } else if (!throwException) {
            // fail quietly
            m_session.log(SessionLog.WARNING, SessionLog.METADATA, EntityManagerSetupImpl.ERROR_LOADING_XML_FILE, new Object[] {mappingFile, e});
        } else {
            // fail loudly
            m_session.handleException(e);
        }
    }
    
    /**
     * INTERNAL:
     * This method is responsible for discovering all the entity classes for 
     * this PU and adding corresponding MetadataDescriptor in the 
     * MetadataProject.
     * 
     * This method will also gather all the weavable classes for this PU. 
     * Currently, entity and embeddable classes are weavable.
     * 
     * NOTE: The order of processing should not be changed as the steps are
     * dependent on one another.
     */
    protected void initPersistenceUnitClasses() {        
        // 1 - Iterate through the classes that are defined in the <mapping>
        // files and add them to the map. This will merge the accessors where
        // necessary.
        HashMap<String, EntityAccessor> entities = new HashMap<String, EntityAccessor>();
        HashMap<String, EmbeddableAccessor> embeddables = new HashMap<String, EmbeddableAccessor>();
        
        for (XMLEntityMappings entityMappings : m_project.getEntityMappings()) {
            entityMappings.initPersistenceUnitClasses(entities, embeddables);  
        }

        // 2 - Iterate through all the XML entities and add them to the project 
        // and apply any persistence unit defaults.
        for (EntityAccessor entity : entities.values()) {
            // This will apply global persistence unit defaults.
            m_project.addEntityAccessor(entity);
            
            // This will override any global settings.
            entity.getEntityMappings().processEntityMappingsDefaults(entity);
        }

        // 3 - Iterate though all the XML embeddables and add them to the 
        // project and apply any persistence unit defaults.
        for (EmbeddableAccessor embeddable : embeddables.values()) {
            // This will apply global persistence unit defaults.
            m_project.addEmbeddableAccessor(embeddable);
            
            // This will override any global settings.
            embeddable.getEntityMappings().processEntityMappingsDefaults(embeddable);
        }

        // Check if the xml-mapping-metadata-complete was declared.  If so, then ignore <class> entries defined
        // in the persistence unit, as by definition only elements declared in ORM XML are to be permitted.
        if (m_project.getPersistenceUnitMetadata() != null && m_project.getPersistenceUnitMetadata().isXMLMappingMetadataComplete()) {
            return;
        }
        
        // 4 - Iterate through the classes that are referenced from the
        // persistence.xml file.
        PersistenceUnitInfo persistenceUnitInfo = m_project.getPersistenceUnitInfo();
        List<String> classNames = new ArrayList<String>();
        
        // Add all the <class> specifications.
        classNames.addAll(persistenceUnitInfo.getManagedClassNames());

        // Add all the classes from the <jar> specifications.
        for (URL url : persistenceUnitInfo.getJarFileUrls()) {
            classNames.addAll(PersistenceUnitProcessor.getClassNamesFromURL(url, m_loader, null));
        }

        // Add all the classes off the classpath at the persistence unit root url.
        Set<String> unlistedClasses = Collections.EMPTY_SET;
        if (! persistenceUnitInfo.excludeUnlistedClasses()) {
            unlistedClasses = PersistenceUnitProcessor.getClassNamesFromURL(persistenceUnitInfo.getPersistenceUnitRootUrl(), m_loader, m_predeployProperties);
        }
        
        // 5 - Go through all the class names we found and add those classes 
        // that have not yet been added. Be sure to check that the accessor
        // does not already exist since adding an accessor will merge its 
        // contents with an existing accessor and we only want that to happen 
        // in the XML case. Also, don't add an entity accessor if an embeddable
        // accessor to the same class exists (and vice versa). XML accessors
        // are loaded first, so preserve what we find there.
        Iterator<String> iterator = classNames.iterator();
        boolean unlisted = false;
        while (iterator.hasNext() || !unlisted) {
            if (!iterator.hasNext() && !unlisted) {
                iterator = unlistedClasses.iterator();
                unlisted = true;
            }
            if (iterator.hasNext()) {
                String className = iterator.next();
                MetadataClass candidateClass = m_factory.getMetadataClass(className, unlisted);
                // JBoss Bug 227630: Do not process a null class whether it was from a 
                // NPE or a CNF, a warning or exception is thrown in loadClass() 
                if (candidateClass != null) {
                    if (PersistenceUnitProcessor.isEntity(candidateClass) && ! m_project.hasEntity(candidateClass) && ! m_project.hasEmbeddable(candidateClass)) {
                        m_project.addEntityAccessor(new EntityAccessor(PersistenceUnitProcessor.getEntityAnnotation(candidateClass), candidateClass, m_project));
                    } else if (PersistenceUnitProcessor.isEmbeddable(candidateClass) && ! m_project.hasEmbeddable(candidateClass) && ! m_project.hasEntity(candidateClass)) {
                        m_project.addEmbeddableAccessor(new EmbeddableAccessor(PersistenceUnitProcessor.getEmbeddableAnnotation(candidateClass), candidateClass, m_project));
                    } else if (PersistenceUnitProcessor.isStaticMetamodelClass(candidateClass)) {
                        m_project.addStaticMetamodelClass(PersistenceUnitProcessor.getStaticMetamodelAnnotation(candidateClass), candidateClass);
                    } else if (PersistenceUnitProcessor.isConverter(candidateClass) && ! m_project.hasConverterAccessor(candidateClass)) {
                        m_project.addConverterAccessor(new ConverterAccessor(PersistenceUnitProcessor.getConverterAnnotation(candidateClass), candidateClass, m_project));
                    } else if (PersistenceUnitProcessor.isMappedSuperclass(candidateClass) && ! m_project.hasMappedSuperclass(candidateClass)) {
                        // ensure mapped superclasses will be added to the metamodel even if they do not have entity subclasses
                        MetadataDescriptor metadataDescriptor = new MetadataDescriptor(candidateClass);
                        // add the mapped superclass to keep track of it in case it is not processed later (has no subclasses).  
                        m_session.getProject().addMappedSuperclass(candidateClass.getName(), metadataDescriptor.getClassDescriptor(), false);
                    }
                }
            }
        }
    }
    
    /**
     * INTERNAL:
     * This method is responsible for figuring out list of mapping files to
     * read into XMLEntityMappings objects and store on the project. Note,
     * the order the files are discovered and read is very important so do
     * not change the order of invocation.
     */
    public void loadMappingFiles(boolean throwExceptionOnFail) {
        // Read all the standard XML mapping files first.
        loadStandardMappingFiles(MetadataHelper.JPA_ORM_FILE);
        
        // Read all the explicitly specified mapping files second.
        loadSpecifiedMappingFiles(throwExceptionOnFail);

        // Read all the standard eclipselink files last (if the user hasn't
        // explicitly excluded them). The eclipselink orm files will be 
        // processed last therefore allowing them to override and merge 
        // metadata where necessary. Note: we want the eclipselink orm
        // metadata to merge into standard jpa files and not vice versa. 
        // Loading them last will ensure this happens.
        Boolean excludeEclipseLinkORM = Boolean.valueOf((String) m_project.getPersistenceUnitInfo().getProperties().get(PersistenceUnitProperties.EXCLUDE_ECLIPSELINK_ORM_FILE));
        if (! excludeEclipseLinkORM) {
            loadStandardMappingFiles(MetadataHelper.ECLIPSELINK_ORM_FILE);
        }

        if (m_metadataSource !=null) {
            XMLEntityMappings entityMappings = m_metadataSource.getEntityMappings(this.m_predeployProperties, m_loader, m_session.getSessionLog());
            if (entityMappings != null) {
                m_project.addEntityMappings(entityMappings);
            }
        }
    }

    /**
     * INTERNAL:
     */
    protected void loadSpecifiedMappingFiles(boolean throwExceptionOnFail) {
        PersistenceUnitInfo puInfo = m_project.getPersistenceUnitInfo();
        
        for (String mappingFileName : puInfo.getMappingFileNames()) {
            try {
                Enumeration<URL> mappingFileURLs = m_loader.getResources(mappingFileName);
                
                if (!mappingFileURLs.hasMoreElements()){
                    mappingFileURLs = m_loader.getResources("/./" + mappingFileName);
                }
                
                if (mappingFileURLs.hasMoreElements()) {
                    URL nextURL = mappingFileURLs.nextElement();
                    if (nextURL == null) {
                        nextURL = mappingFileURLs.nextElement();                        
                    }

                    if (mappingFileURLs.hasMoreElements()) {
                        // Switched to warning, same file can be on the classpath twice in some deployments,
                        // should not be an error.
                        Throwable throwable = ValidationException.nonUniqueMappingFileName(puInfo.getPersistenceUnitName(), mappingFileName);
                        getSessionLog().logThrowable(SessionLog.FINER, SessionLog.METADATA, throwable);
                    }
                    
                    // Read the document through OX and add it to the project.
                    m_project.addEntityMappings(XMLEntityMappingsReader.read(nextURL, m_loader, m_project.getPersistenceUnitInfo().getProperties()));
                } else {
                    handleORMException(ValidationException.mappingFileNotFound(puInfo.getPersistenceUnitName(), mappingFileName), mappingFileName, throwExceptionOnFail);
                }
            } catch (IOException e) {
                handleORMException(PersistenceUnitLoadingException.exceptionLoadingORMXML(mappingFileName, e), mappingFileName, throwExceptionOnFail);
            }
        }
    }
    
    /**
     * INTERNAL:
     */
    protected void loadStandardMappingFiles(String ormXMLFile) {
        PersistenceUnitInfo puInfo = m_project.getPersistenceUnitInfo();
        Collection<URL> rootUrls = new HashSet<URL>(puInfo.getJarFileUrls());
        rootUrls.add(puInfo.getPersistenceUnitRootUrl());
        
        for (URL rootURL : rootUrls) {
            getSessionLog().log(SessionLog.FINER, SessionLog.METADATA, "searching_for_default_mapping_file", new Object[] { ormXMLFile, rootURL }, true);
            URL ormURL = null;

            Archive par = null;
            try {
                par = PersistenceUnitProcessor.getArchiveFactory(m_loader).createArchive(rootURL, null);
                
                if (par != null) {
                    ormURL = par.getEntryAsURL(ormXMLFile);

                    if (ormURL != null) {
                        getSessionLog().log(SessionLog.FINER, SessionLog.METADATA, "found_default_mapping_file", new Object[] { ormURL, rootURL }, true);

                        // Read the document through OX and add it to the project., pass persistence unit properties for any orm properties set there
                        XMLEntityMappings entityMappings = XMLEntityMappingsReader.read(ormURL, m_loader, m_project.getPersistenceUnitInfo().getProperties());
                        entityMappings.setIsEclipseLinkORMFile(ormXMLFile.equals(MetadataHelper.ECLIPSELINK_ORM_FILE));
                        m_project.addEntityMappings(entityMappings);
                    }
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            } catch (URISyntaxException e) {
                throw new RuntimeException(e);
            } finally {
                if (par != null) {
                    par.close();
                }
            }
        }
    }

    /**
     * INTERNAL:
     * Return the SessionLog from the Session. If the session is null,
     * return the AbstractSessionLog's SessionLog.
     * @return SessionLog
     */
    protected SessionLog getSessionLog() {
        if (m_session != null) {
            return m_session.getSessionLog();
        } else {
            return AbstractSessionLog.getLog();
        }
    }
    
    /**
     * INTERNAL: 
     * Process the customizer for those entities and embeddables that have one
     * defined. This must be the last thing called on this processor before
     * cleanup.
     */
    public void processCustomizers() {
        for (ClassAccessor classAccessor: m_project.getAccessorsWithCustomizer()) {
            DescriptorCustomizer customizer = (DescriptorCustomizer) MetadataHelper.getClassInstance(classAccessor.getCustomizerClass().getName(), m_loader);
            
            try {
                customizer.customize(classAccessor.getDescriptor().getClassDescriptor());
            } catch (Exception e) {
                getSessionLog().logThrowable(SessionLog.FINER, SessionLog.METADATA, e);
            }
        }
    }

    /**
     * INTERNAL:
     * Performs the initialization of the persistence unit classes and then
     * processes the xml metadata.
     * Note: Do not change the order of invocation of various methods.
     */
    public void processEntityMappings(PersistenceUnitProcessor.Mode mode) {
        if (mode == PersistenceUnitProcessor.Mode.ALL || mode == PersistenceUnitProcessor.Mode.COMPOSITE_MEMBER_INITIAL) {
            m_factory = new MetadataAsmFactory(m_project.getLogger(), m_loader);
            
            // 1 - Process persistence unit meta data/defaults defined in ORM XML 
            // instance documents in the persistence unit. If multiple conflicting 
            // persistence unit meta data is found, this call will throw an 
            // exception. The meta data we find here will be applied in the
            // initialize call below.
            for (XMLEntityMappings entityMappings : m_project.getEntityMappings()) {
                // Since this our first iteration through the entity mappings list,
                // set the project and loader references. This is very important
                // and must be done first!
                entityMappings.setLoader(m_loader);
                entityMappings.setProject(m_project);
                entityMappings.setMetadataFactory(m_factory);
                
                // Process the persistence unit metadata if defined.
                entityMappings.processPersistenceUnitMetadata();
            }
            
            // 2 - Initialize all the persistence unit class with the meta data we
            // processed in step 1.
            initPersistenceUnitClasses();

            // 3 - Now process the entity mappings metadata.
            for (XMLEntityMappings entityMappings : m_project.getEntityMappings()) {
                entityMappings.process();
            }
        }
    }

    /**
     * INTERNAL:
     * Process the ORM metadata on this processors metadata project 
     * (representing a single persistence-unit)
     */
    public void processORMMetadata(PersistenceUnitProcessor.Mode mode) {
        if (mode == Mode.ALL || mode == Mode.COMPOSITE_MEMBER_INITIAL) {
            m_project.processStage1();
            m_project.processStage2();
        }
        
        if (mode != PersistenceUnitProcessor.Mode.COMPOSITE_MEMBER_INITIAL) {
            m_project.processStage3(mode);
        }
    }

    /**
     * INTERNAL:
     * Use this method to set the correct class loader that should be used
     * during processing. Currently, the class loader should only change
     * once, from preDeploy to deploy.
     */
    public void setClassLoader(ClassLoader loader) {
        m_loader = loader;
        m_factory.setLoader(loader);
        // Update the loader on all the entity mappings for this project.
        for (XMLEntityMappings entityMappings : m_project.getEntityMappings()) {
            entityMappings.setLoader(m_loader);
        }
    }

    /**
     * INTERNAL:
     * Use this method to set the MetadataSource class to use for loading
     * extensible mappings
     */
    public void setMetadataSource(MetadataSource source){
        m_metadataSource = source;
    }
}
