FileDocCategorySizeDatePackage
ClassAccessor.javaAPI DocGlassfish v2 API62794Tue May 22 16:54:26 BST 2007oracle.toplink.essentials.internal.ejb.cmp3.metadata.accessors

ClassAccessor.java

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * // Copyright (c) 1998, 2007, Oracle. All rights reserved.
 * 
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
package oracle.toplink.essentials.internal.ejb.cmp3.metadata.accessors;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.persistence.AssociationOverride;
import javax.persistence.AssociationOverrides;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.ExcludeDefaultListeners;
import javax.persistence.ExcludeSuperclassListeners;
import javax.persistence.IdClass;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.MappedSuperclass;
import javax.persistence.NamedNativeQueries;
import javax.persistence.NamedNativeQuery;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PostRemove;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;
import javax.persistence.SecondaryTable;
import javax.persistence.SecondaryTables;
import javax.persistence.SqlResultSetMapping;
import javax.persistence.SqlResultSetMappings;
import javax.persistence.Table;
import javax.persistence.Transient;

import oracle.toplink.essentials.descriptors.ClassDescriptor;

import oracle.toplink.essentials.internal.ejb.cmp3.metadata.accessors.MetadataAccessor;

import oracle.toplink.essentials.internal.ejb.cmp3.metadata.accessors.objects.MetadataAccessibleObject;
import oracle.toplink.essentials.internal.ejb.cmp3.metadata.accessors.objects.MetadataClass;
import oracle.toplink.essentials.internal.ejb.cmp3.metadata.accessors.objects.MetadataField;
import oracle.toplink.essentials.internal.ejb.cmp3.metadata.accessors.objects.MetadataMethod;

import oracle.toplink.essentials.internal.ejb.cmp3.metadata.columns.MetadataColumn;
import oracle.toplink.essentials.internal.ejb.cmp3.metadata.columns.MetadataDiscriminatorColumn;
import oracle.toplink.essentials.internal.ejb.cmp3.metadata.columns.MetadataJoinColumn;
import oracle.toplink.essentials.internal.ejb.cmp3.metadata.columns.MetadataJoinColumns;
import oracle.toplink.essentials.internal.ejb.cmp3.metadata.columns.MetadataPrimaryKeyJoinColumn;
import oracle.toplink.essentials.internal.ejb.cmp3.metadata.columns.MetadataPrimaryKeyJoinColumns;

import oracle.toplink.essentials.internal.ejb.cmp3.metadata.listeners.MetadataEntityClassListener;
import oracle.toplink.essentials.internal.ejb.cmp3.metadata.listeners.MetadataEntityListener;

import oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataHelper;
import oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataProcessor;
import oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataDescriptor;

import oracle.toplink.essentials.internal.ejb.cmp3.metadata.queries.MetadataEntityResult;
import oracle.toplink.essentials.internal.ejb.cmp3.metadata.queries.MetadataFieldResult;
import oracle.toplink.essentials.internal.ejb.cmp3.metadata.queries.MetadataNamedNativeQuery;
import oracle.toplink.essentials.internal.ejb.cmp3.metadata.queries.MetadataNamedQuery;
import oracle.toplink.essentials.internal.ejb.cmp3.metadata.queries.MetadataSQLResultSetMapping;

import oracle.toplink.essentials.internal.ejb.cmp3.metadata.tables.MetadataSecondaryTable;
import oracle.toplink.essentials.internal.ejb.cmp3.metadata.tables.MetadataTable;

import oracle.toplink.essentials.internal.ejb.cmp3.xml.accessors.XMLMappedSuperclassAccessor;
import oracle.toplink.essentials.internal.ejb.cmp3.xml.listeners.XMLEntityListener;
import oracle.toplink.essentials.internal.ejb.cmp3.xml.XMLConstants;
import oracle.toplink.essentials.internal.ejb.cmp3.xml.XMLHelper;

import oracle.toplink.essentials.internal.helper.DatabaseField;
import oracle.toplink.essentials.internal.helper.Helper;

import oracle.toplink.essentials.queryframework.ColumnResult;
import oracle.toplink.essentials.queryframework.EntityResult;
import oracle.toplink.essentials.queryframework.FieldResult;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * A class accessor.
 * 
 * @author Guy Pelletier
 * @since TopLink EJB 3.0 Reference Implementation
 */
public class ClassAccessor extends NonRelationshipAccessor {
    // This is the parent class that defines the inheritance strategy, and
    // not necessarilly the immediate parent class.
    protected Class m_inheritanceParentClass;
    protected Boolean m_isInheritanceSubclass;
    protected List<ClassAccessor> m_mappedSuperclasses;
    
    /**
     * INTERNAL:
     */
    public ClassAccessor(MetadataAccessibleObject accessibleObject, MetadataProcessor processor, MetadataDescriptor descriptor) {
        super(accessibleObject, processor, descriptor);
    }
    
    /**
     * INTERNAL:
	 * Add multiple fields to the descriptor. Called from either @Inheritance 
     * or @SecondaryTable context.
	 */
    protected void addMultipleTableKeyFields(MetadataPrimaryKeyJoinColumns primaryKeyJoinColumns, String PK_CTX, String FK_CTX) {
        // ProcessPrimaryKeyJoinColumns will validate the primary key join
        // columns passed in and will return a list of 
        // MetadataPrimaryKeyJoinColumn.
        for (MetadataPrimaryKeyJoinColumn primaryKeyJoinColumn : processPrimaryKeyJoinColumns(primaryKeyJoinColumns)) {
            // In an inheritance case this call will return the pk field on the
            // root class of the inheritance hierarchy. Otherwise in a secondary
            // table case it's the primary key field name off our own descriptor.
            String defaultPKFieldName = m_descriptor.getPrimaryKeyFieldName();

            DatabaseField pkField = primaryKeyJoinColumn.getPrimaryKeyField();
            pkField.setName(getName(pkField, defaultPKFieldName, PK_CTX));

            DatabaseField fkField = primaryKeyJoinColumn.getForeignKeyField();
            fkField.setName(getName(fkField, pkField.getName(), FK_CTX));
        
            if (fkField.getName().equals(pkField.getName())) {
                // Add a multiple table primary key to the descriptor.
                m_descriptor.addMultipleTablePrimaryKeyField(pkField, fkField);
            } else {
                // Add a multiple table foreign key to the descriptor.
                m_descriptor.addMultipleTableForeignKeyField(pkField, fkField);
            }
        }
    }
    
    /**
     * INTERNAL:
     * Create and return the appropriate accessor based on the accessible 
     * object given. Order of checking is important, careful when modifying
     * or adding, check what the isXyz call does to determine if the accessor
     * is of type xyz.
     */
    protected MetadataAccessor buildAccessor(MetadataAccessibleObject accessibleObject) {
        MetadataAccessor accessor = m_descriptor.getAccessorFor(accessibleObject.getAttributeName());
        
        if (accessor == null) {
            if (MetadataHelper.isBasic(accessibleObject, m_descriptor)) {
                return new BasicAccessor(accessibleObject, this);
            } else if (MetadataHelper.isEmbedded(accessibleObject, m_descriptor)) {
                return new EmbeddedAccessor(accessibleObject, this);
            } else if (MetadataHelper.isEmbeddedId(accessibleObject, m_descriptor)) {
                return new EmbeddedIdAccessor(accessibleObject, this);
            } else if (MetadataHelper.isManyToMany(accessibleObject, m_descriptor)) {
                return new ManyToManyAccessor(accessibleObject, this);
            } else if (MetadataHelper.isManyToOne(accessibleObject, m_descriptor)) {
                return new ManyToOneAccessor(accessibleObject, this);
            } else if (MetadataHelper.isOneToMany(accessibleObject, m_descriptor)) {
                // A OneToMany can currently default, that is, doesn't require
                // an annotation to be present.
                return new OneToManyAccessor(accessibleObject, this);
            } else if (MetadataHelper.isOneToOne(accessibleObject, m_descriptor)) {
                // A OneToOne can currently default, that is, doesn't require
                // an annotation to be present.
                return new OneToOneAccessor(accessibleObject, this);
            } else {
                // Default case (everything else currently falls into this)
                return new BasicAccessor(accessibleObject, this);
            }
        } else {
            return accessor;
        }
    }
    
    /**
     * INTERNAL:
     * Clear the mapped supeclasses. Do this after the class loader has changed.
     * The list of mapped superclasses is lazily initialized.
     */
    public void clearMappedSuperclasses() {
        m_mappedSuperclasses = null;     
    }
    
    /**
     * INTERNAL: (Overridden in XMLClassAccessor)
     * Return the discriminator value for this accessor.
     */
    protected String getDiscriminatorValue() {
        DiscriminatorValue discriminatorValue = getAnnotation(DiscriminatorValue.class);
        
        if (discriminatorValue == null) {
            return null;
        } else {
            return discriminatorValue.value();
        }
    }
    
    /**
     * INTERNAL: (Overridden in XMLClassAccessor)
     * 
     * Return the name of this entity class.
     */
    protected String getEntityName() {
        Entity entity = getAnnotation(Entity.class);
        return (entity == null) ? "" : entity.name();
    }
    
    /**
     * INTERNAL:
     * 
     * This method returns the first parnt entity in an inheritance hierarchy 
     * that defines the inheritance strategy. That is, has an @Inheritance or
     * inheritance xml element.
     */
	public Class getInheritanceParentClass() {
        if (m_inheritanceParentClass == null) {
            Class lastParent = null;
            Class parent = getJavaClass().getSuperclass();
        
            while (parent != Object.class) {
                if (hasInheritance(parent) || m_project.containsDescriptor(parent)) {
                    if (lastParent == null) {
                        // Set the immediate parent class on the descriptor.
                        m_descriptor.setParentClass(parent);
                    }
                    
                    lastParent = parent;
                    
                    if (hasInheritance(parent)) {
                        break; // stop looking.
                    }
                } 
                
                parent = parent.getSuperclass();
            }
        
            // Finally set whatever we found as the inheritance parent class. 
            // Which may be null.
            m_inheritanceParentClass = lastParent;
        }
  
        return m_inheritanceParentClass;
    }
    
    /**
     * INTERNAL: 
     * Store the descriptor metadata for the root of our inheritance hierarchy.
     */
    public MetadataDescriptor getInheritanceParentDescriptor() {
        return m_project.getDescriptor(getInheritanceParentClass());
    }
    
    /**
     * INTERNAL: (Overridden in XMLClassAccessor)
     * Return the inheritance strategy. This method should only be called
     * on the root of the inheritance hierarchy.
     */
    protected String getInheritanceStrategy() {
        Inheritance inheritance = getAnnotation(Inheritance.class);
        
        if (inheritance == null) {
            return "";
        } else {
            return inheritance.strategy().name();   
        }
    }
    
    /**
     * INTERNAL: (OVERRIDE)
     * Return the java class that defines this accessor. It may be an
     * entity, embeddable or mapped superclass.
     */
    public Class getJavaClass() {
        return (Class) getAnnotatedElement();
    }
    
    /**
     * INTERNAL:
     * Build a list of classes that are decorated with a @MappedSuperclass.
     */
	public List<ClassAccessor> getMappedSuperclasses() {
        if (m_mappedSuperclasses == null) {
            m_mappedSuperclasses = new ArrayList<ClassAccessor>();
            
            Class parent = getJavaClass().getSuperclass();
        
            while (parent != Object.class) {
                if (isInheritanceSubclass() && hasEntity(parent)) {
                    // In an inheritance case we don't want to keep looking
                    // for mapped superclasses if they are not directly above
                    // us before the next entity in the hierarchy.
                    break;
                } else if (m_project.hasMappedSuperclass(parent)) {
                    Node node = m_project.getMappedSuperclassNode(parent);
                    XMLHelper helper = m_project.getMappedSuperclassHelper(parent);                    
                    m_mappedSuperclasses.add(new XMLMappedSuperclassAccessor(new MetadataClass(parent), node, helper, m_processor, m_descriptor));
                } else if (isAnnotationPresent(MappedSuperclass.class, parent)) {
                    m_mappedSuperclasses.add(new MappedSuperclassAccessor(new MetadataClass(parent), m_processor, m_descriptor));
                }
                
                parent = parent.getSuperclass();
            }
        }
        
        return m_mappedSuperclasses;
    }
    
    /**
     * INTERNAL: (Overridden in XMLClassAccessor)
     * 
     * Return true if the class has an @Entity.
     */
    protected boolean hasEntity(Class cls) {
        return isAnnotationPresent(Entity.class, cls);
    }
    
    /**
     * INTERNAL:
     * 
     * Return true if this class accessor has an inheritance specifications.
     */
    protected boolean hasInheritance() {
        return hasInheritance(getJavaClass());
    }
    
    /**
     * INTERNAL: (Overridden in XMLClassAccessor)
     * 
     * Return true if the class has an @Inheritance.
     */
    protected boolean hasInheritance(Class entityClass) {
        return isAnnotationPresent(Inheritance.class, entityClass);
    }
    
    /**
     * INTERNAL:
     */
     public boolean isClass() {
     	return true;
     }
     
    /**
     * INTERNAL:
     */
    public boolean isInheritanceSubclass() {
        if (m_isInheritanceSubclass == null) {
            m_isInheritanceSubclass = new Boolean(getInheritanceParentClass() != null);
        }
        
        return m_isInheritanceSubclass;
    }
     
     /**
     * INTERNAL:
     */
    protected boolean isTransient(AnnotatedElement annotatedElement, int modifier) {
        if (isAnnotationPresent(Transient.class, annotatedElement)) {
            if (MetadataHelper.getDeclaredAnnotationsCount(annotatedElement, m_descriptor) > 1) {
                m_validator.throwMappingAnnotationsAppliedToTransientAttribute(annotatedElement);
            }
            
            return true;
        } else if (Modifier.isTransient(modifier)) {
            if (MetadataHelper.getDeclaredAnnotationsCount(annotatedElement, m_descriptor) > 0) {
                m_validator.throwMappingAnnotationsAppliedToTransientAttribute(annotatedElement);
            }
            
            return true;
        }
        
        return false;
    }

    /**
     * INTERNAL:
     * Return true is this annotated element is not marked transient, static or 
     * abstract.
     */
    protected boolean isValidPersistenceElement(AnnotatedElement annotatedElement, int modifiers) {
        return ! (isTransient(annotatedElement, modifiers) || Modifier.isStatic(modifiers) || Modifier.isAbstract(modifiers));
    }
    
    /**
     * INTERNAL:
     * Check to see if this is a valid field to process for persistence. It is 
     * valid if it is not static, transient or has a @Transient specified.
     */
    protected boolean isValidPersistenceField(Field field) {
        return (isValidPersistenceElement(field, field.getModifiers()));
    }
    
    /**
     * INTERNAL:
     * Check to see if this is a valid method to process for persistence. It is 
     * valid if it is not static, transient or has a @Transient specified.
     */
    protected boolean isValidPersistenceMethod(Method method) {
        // Ignore methods marked transient, static or abstract.
        if (isValidPersistenceElement(method, method.getModifiers())) {
            // Look for methods that begin with "get" or "is", ignore all others.
            String methodName = method.getName();
            if (MetadataHelper.isValidPersistenceMethodName(methodName)) {
                // Ignore get methods with parameters.
                if (method.getParameterTypes().length > 0) {
                    return false;
                }
            
                Method setMethod = MetadataHelper.getSetMethod(method, getJavaClass());
            
                if (setMethod == null) {
                    if (MetadataHelper.getDeclaredAnnotationsCount(method, m_descriptor) > 0) {
                        // We decorated the property with annotations, but have 
                        // no corresponding setter property.
                        m_validator.throwNoCorrespondingSetterMethodDefined(getJavaClass(), method);
                    }
                } else {
                    return true;
                }
            }
        }
        
        return false;
    }
    
    /**
     * INTERNAL:
     * Process the items of interest on an entity or embeddable class. The 
     * order of processing is important, care must be taken if changes must 
     * be made.
     */
    public void process() {        
        if (hasEntity(getJavaClass())) {
            // This accessor represents an @Entity class.
            // Set the ignore flags for items that are already defined.
            m_descriptor.setIgnoreFlags();
            
            // Process the @Entity.
            processEntity();
            
            // Process the @Table and @Inheritance.
            processTableAndInheritance();
            
            // Process the common class level attributes that an entity or
            // mapped superclass may define. This should be done before the
            // processMappedSuperclasses call since it will call this method 
            // also. We want to be able to grab the metadata off the actual 
            // entity class first because it needs to override any settings 
            // from the mapped superclass and may need to log a warning.
            processClassMetadata();
                
            // Process the @MappedSuperclass(es).
            processMappedSuperclasses();
                    
            // Process the accessors on this entity.
            processAccessors();
            
            // Validate we found a primary key.
            validatePrimaryKey();
                    
            // Process the @SecondaryTable(s).
            processSecondaryTables();
        } else {
            // This accessor represents an @Embeddable class
            m_descriptor.setIsEmbeddable();
            
            // Process the accessors on this embeddable.
            processAccessors();
        }
    }
    
    /**
     * INTERNAL:
     * Process an accessor method or field. Relationship accessors will be 
     * stored for later processing.
     */
    protected void processAccessor(MetadataAccessor accessor) {
        if (! accessor.isProcessed()) {
            // Store the accessor for later retrieval.
            m_descriptor.addAccessor(accessor);
            
            if (accessor.isRelationship()) {
                // Store the relationship accessors for later processing.
                m_project.addRelationshipDescriptor(m_descriptor);
            } else {
                accessor.process();
                accessor.setIsProcessed();
            }
        }
    }
    
    /**
     * INTERNAL:
     * Create mappings from the fields directly.
     */
    protected void processAccessorFields() {
        for (Field field : MetadataHelper.getFields(getJavaClass())) {
            if (isValidPersistenceField(field)) {
                processAccessor(buildAccessor(new MetadataField(field)));
            }
        }
    }
    
    /**
     * INTERNAL:
     * Create mappings via the class properties.
     */
    protected void processAccessorMethods() {
        for (Method method : MetadataHelper.getDeclaredMethods(getJavaClass())) {
            if (isValidPersistenceMethod(method)) {
                processAccessor(buildAccessor(new MetadataMethod(method)));
            }
        }
    }
    
    /**
     * INTERNAL:
     * Process the accessors for the given class.
     */
    protected void processAccessors() {
        // Process the fields or methods on the class.
        if (m_descriptor.usesPropertyAccess()) {
            processAccessorMethods();
        } else {
            processAccessorFields();
        }
    }
    
    /**
     * INTERNAL:
     * Process an @AssociationOverride for an Entity (or MappedSuperclass) 
     * that inherits from a MappedSuperclass.
     */
    protected void processAssociationOverride(String attributeName, MetadataJoinColumns joinColumns) {
        // Add association overrides from XML as we find them.
        if (joinColumns.loadedFromXML()) {
            m_descriptor.addAssociationOverride(attributeName, joinColumns);
        } else {
            // Association override from annotations should not override those 
            // loaded from XML.
            MetadataJoinColumns existingJoinColumns = m_descriptor.getAssociationOverrideFor(attributeName);
            
            if (existingJoinColumns == null || ! existingJoinColumns.loadedFromXML()) {
                m_descriptor.addAssociationOverride(attributeName, joinColumns);
            } else {
                // WIP should log a warning.
            }
        }
    }
    
    /**
     * INTERNAL: (Overriden in XMLClassAccessor)
     * Process an @AssociationOverrides for an Entity (or MappedSuperclass) 
     * that inherits from a MappedSuperclass.
     * 
     * It will also look for an @AssociationOverride.
     */
    protected void processAssociationOverrides() {
        // Look for an @AssociationOverrides.
        AssociationOverrides associationOverrides = getAnnotation(AssociationOverrides.class);
        if (associationOverrides != null) {
            for (AssociationOverride associationOverride : associationOverrides.value()) {
                processAssociationOverride(associationOverride.name(), new MetadataJoinColumns(associationOverride.joinColumns()));
            }
        }
        
        // Look for an @AssociationOverride.
        AssociationOverride associationOverride = getAnnotation(AssociationOverride.class);
        if (associationOverride != null) {
            processAssociationOverride(associationOverride.name(), new MetadataJoinColumns(associationOverride.joinColumns()));
        }
    }
    
    /**
     * INTERNAL:
     * Process the @AttributeOverrides and @AttributeOverride for an Entity (or 
     * MappedSuperclass) that inherits from a MappedSuperclass.
     */
    protected void processAttributeOverride(MetadataColumn column) {
        String attributeName = column.getAttributeName();
        
        // Add attribute overrides from XML as we find them.
        if (column.loadedFromXML()) {
            m_descriptor.addAttributeOverride(column);
        } else {
            // Attribute overrides from annotations should not override
            // those loaded from XML.
            MetadataColumn existingColumn = m_descriptor.getAttributeOverrideFor(attributeName);
            
            if (existingColumn == null || ! existingColumn.loadedFromXML()) {
                m_descriptor.addAttributeOverride(column);
            } else {
                // WIP should log a warning.
            }
        }
    }
    
    /**
     * INTERNAL: (Overridden in XMLClassAccessor)
     * Process the @AttributeOverrides and @AttributeOverride for an Entity (or 
     * MappedSuperclass) that inherits from a MappedSuperclass.
     */
    protected void processAttributeOverrides() {
        // Look for an @AttributeOverrides.
        AttributeOverrides attributeOverrides = getAnnotation(AttributeOverrides.class);	
        if (attributeOverrides != null) {
            for (AttributeOverride attributeOverride : attributeOverrides.value()) {
                processAttributeOverride(new MetadataColumn(attributeOverride, getAnnotatedElement()));
            }
        }
        
        // Look for an @AttributeOverride.
        AttributeOverride attributeOverride = getAnnotation(AttributeOverride.class);
        if (attributeOverride != null) {
            processAttributeOverride(new MetadataColumn(attributeOverride, getAnnotatedElement()));
        }
    }
    
    /**
     * INTERNAL:
     * Process the array of methods for lifecyle callback events and set them
     * on the given event listener.
     */
    protected void processCallbackMethods(Method[] candidateMethods, MetadataEntityListener listener) {
        for (Method method : candidateMethods) {
            if (isAnnotationPresent(PostLoad.class, method)) {
                setPostLoad(method, listener);
            }
            
            if (isAnnotationPresent(PostPersist.class, method)) {
                setPostPersist(method, listener);
            }
            
            if (isAnnotationPresent(PostRemove.class, method)) {
                setPostRemove(method, listener);
            }
            
            if (isAnnotationPresent(PostUpdate.class, method)) {
                setPostUpdate(method, listener);
            }
            
            if (isAnnotationPresent(PrePersist.class, method)) {
                setPrePersist(method, listener);
            }
            
            if (isAnnotationPresent(PreRemove.class, method)) {
                setPreRemove(method, listener);
            }
            
            if (isAnnotationPresent(PreUpdate.class, method)) {
                setPreUpdate(method, listener);
            }
        }
    }
    
    /**
     * INTERNAL:
     * 
     * Process the items of interest on an entity or mapped superclass class. 
     */
    protected void processClassMetadata() {
        // Process the @AttributeOverrides and @AttributeOverride.
        processAttributeOverrides();
                    
        // Process the @AssociationOverrides and @AssociationOverride.
        processAssociationOverrides();
        
        // Process the @NamedQueries and @NamedQuery.
        processNamedQueries();
                    
        // Process the @NamedNativeQueries and @NamedNativeQuery.
        processNamedNativeQueries();
                    
        // Process the @SqlRessultSetMapping.
        processSqlResultSetMappings();
                    
        // Process the @TableGenerator.
        processTableGenerator();
            
        // Process the @SequenceGenerator
        processSequenceGenerator();
                    
        // Process the @IdClass (pkClass).
        processIdClass();
        
        // Process the @ExcludeDefaultListeners.
        processExcludeDefaultListeners();
        
        // Process the @ExcludeSuperclassListeners.
        processExcludeSuperclassListeners();
    }
    
    /**
     * INTERNAL:
     * Process the default listeners defined in XML. This method will process 
     * the class for additional lifecycle callback methods that are decorated 
     * with annotations.
     * 
     * NOTE: We add the default listeners regardless if the exclude default 
     * listeners flag is set. This allows the user to change the exlcude flag 
     * at runtime and have the default listeners available to them.
     */
    protected void processDefaultListeners(ClassLoader loader) {
        Map<XMLHelper, NodeList> defaultListeners = m_project.getDefaultListeners();
        
        for (XMLHelper helper : defaultListeners.keySet()) {
            // Update the class loader.
            helper.setLoader(loader);
            NodeList nodes = defaultListeners.get(helper);
            
            for (int i = 0; i < nodes.getLength(); i++) {
                Node node = nodes.item(i);
                
                // Build an xml entity listener.
                XMLEntityListener listener = new XMLEntityListener(helper.getClassForNode(node), getJavaClass());
                
                // Process the lifecycle callback events from XML.
                Method[] candidateMethods = MetadataHelper.getCandidateCallbackMethodsForDefaultListener(listener);
                processLifecycleEvents(listener, node, helper, candidateMethods);
                
                // Process the candidate callback methods on this listener for
                // additional callback methods decorated with annotations.
                processCallbackMethods(candidateMethods, listener);
        
                // Add the listener to the descriptor.
                m_descriptor.addDefaultEventListener(listener);
            }
		}
    }
    
    /**
     * INTERNAL:
     * Process a @DiscriminatorColumn (if there is one, otherwise default) to 
     * set this classes indication field name for inheritance.
     */
    protected void processDiscriminatorColumn() {
        DiscriminatorColumn discriminatorColumn = getAnnotation(DiscriminatorColumn.class);
        processDiscriminatorColumn(new MetadataDiscriminatorColumn(discriminatorColumn));
    }
    
    /**
     * INTERNAL:
	 * Process a discriminator column to set this class indicatior field name 
     * for inheritance.
	 */
	protected void processDiscriminatorColumn(MetadataDiscriminatorColumn discriminatorColumn) {
        DatabaseField field = new DatabaseField();

        field.setName(getName(discriminatorColumn.getName(), MetadataDiscriminatorColumn.DEFAULT_NAME, m_logger.DISCRIMINATOR_COLUMN));
        field.setLength(discriminatorColumn.getLength());
        field.setTableName(m_descriptor.getPrimaryTableName());
        field.setColumnDefinition(discriminatorColumn.getColumnDefinition());
        field.setType(MetadataHelper.getDiscriminatorType(discriminatorColumn.getDiscriminatorType()));
        
        // Set the class indicator field on the inheritance policy.
        m_descriptor.setClassIndicatorField(field);
    }
	
    /**
     * INTERNAL:
	 * Process a discriminator value to set the class indicator on the root 
     * descriptor of the inheritance hierarchy. 
     * 
     * If there is no discriminator value, the class indicator defaults to 
     * the class name.
	 */
	protected void processDiscriminatorValue() {
        if (! Modifier.isAbstract(getJavaClass().getModifiers())) {
            // Add the indicator to the inheritance root class' descriptor. The
            // default is the short class name.
            String discriminatorValue = getDiscriminatorValue();
            
            if (discriminatorValue == null) {
                m_descriptor.addClassIndicator(getJavaClass(), Helper.getShortClassName(getJavaClassName()));    
            } else {
                m_descriptor.addClassIndicator(getJavaClass(), discriminatorValue);    
            }
        }
    }
    
    /**
	 * INTERNAL:
	 * Process an entity.
	 */
	protected void processEntity() {
        // Don't override existing alias.
        if (m_descriptor.getAlias().equals("")) {
            String alias = getEntityName();
            
            if (alias.equals("")) {
                alias = Helper.getShortClassName(getJavaClassName());
                m_logger.logConfigMessage(m_logger.ALIAS, m_descriptor, alias);
            }

            // Verify that the alias is not a duplicate.
            ClassDescriptor existingDescriptor = getProject().getSession().getProject().getDescriptorForAlias(alias);
            if (existingDescriptor != null) {
                m_validator.throwNonUniqueEntityName(existingDescriptor.getJavaClassName(), m_descriptor.getJavaClassName(), alias);
            }

            // Set the alias on the descriptor and add it to the project.
            m_descriptor.setAlias(alias);
            getProject().getSession().getProject().addAlias(alias, m_descriptor.getClassDescriptor());
        }
	}
    
    /**
     * INTERNAL: (Overridden in XMLCLassAccessor)
     * Process the entity class for lifecycle callback event methods.
     */
    public MetadataEntityListener processEntityEventListener(ClassLoader loader) {
        MetadataEntityClassListener listener = new MetadataEntityClassListener(getJavaClass());
            
        // Check the entity class for lifecycle callback annotations.
        processCallbackMethods(MetadataHelper.getCandidateCallbackMethodsForEntityClass(getJavaClass()), listener);
        
        return listener;
    }
    
    /**
     * INTERNAL: (Overridden in XMLClassAccessor)
     * Process the @EntityListeners for this class accessor.
     */
    public void processEntityListeners(Class entityClass, ClassLoader loader) {
        EntityListeners entityListeners = getAnnotation(EntityListeners.class);
        
        if (entityListeners != null) {
            for (Class entityListener : entityListeners.value()) {
                MetadataEntityListener listener = new MetadataEntityListener(entityListener, entityClass);
                
                // Process the candidate callback methods for this listener ...
                processCallbackMethods(MetadataHelper.getCandidateCallbackMethodsForEntityListener(listener), listener);
                
                // Add the entity listener to the descriptor event manager.    
                m_descriptor.addEntityListenerEventListener(listener);
            }
        }
    }
    
    /**
     * INTERNAL: (Overridden in XMLClassAccessor)
     * Process the @ExcludeDefaultListeners if one is specified (taking
     * metadata-complete into consideration).
     */
    protected void processExcludeDefaultListeners() {
        // Don't overrite a true flag that could be set from a subclass
        // that already exlcuded them.
        if (isAnnotationPresent(ExcludeDefaultListeners.class)) {
            m_descriptor.setExcludeDefaultListeners(true);
        }
    }
    
    /**
     * INTERNAL: (Overridden in XMLClassAccessor)
     * Process the @ExcludeSuperclassListeners if one is specified (taking
     * metadata-complete into consideration).
     */
    protected void processExcludeSuperclassListeners() {
        // Don't overrite a true flag that could be set from a subclass
        // that already exlcuded them.
        if (isAnnotationPresent(ExcludeSuperclassListeners.class)) {
            m_descriptor.setExcludeSuperclassListeners(true);
        }
    }
    
    /**
     * INTERNAL: (Overridden in XMLClassAccessor)
     * 
     * Process an @IdClass.
     */
    protected void processIdClass() {
        IdClass idClass = getAnnotation(IdClass.class);
        
        if (idClass != null) {
            processIdClass(idClass.value(), m_logger.IGNORE_ID_CLASS_ANNOTATION);
        }
    }
    
    /**
     * INTERNAL:
     * 
     * Process an @IdClass or id-class element. It is used to specify composite 
     * primary keys. The primary keys will be processed and stored from the PK 
     * class so that they may be validated against the fields or properties of 
     * the entity bean. The access type of a primary key class is determined by 
     * the access type of the entity for which it is the primary key.
     * 
     * NOTE: the class passed in may be a mapped-superclass or entity.
     */
    protected void processIdClass(Class idClass, String ignoreCtx) {
        if (m_descriptor.ignoreIDs()) {
            m_logger.logWarningMessage(ignoreCtx, m_descriptor, idClass);
        } else {
            m_descriptor.setPKClass(idClass);
                
            if (m_descriptor.usesPropertyAccess()) {
                for (Method method : MetadataHelper.getDeclaredMethods(idClass)) {
                    String methodName = method.getName();
                
                    if (MetadataHelper.isValidPersistenceMethodName(methodName)) {
                        m_descriptor.addPKClassId(MetadataHelper.getAttributeNameFromMethodName(methodName), MetadataHelper.getGenericReturnType(method));
                    }
                }
            } else {
                for (Field field : MetadataHelper.getFields(idClass)) {
                    m_descriptor.addPKClassId(field.getName(), MetadataHelper.getGenericType(field));
                }
            }   
        }
    }
    
    /**
     * INTERNAL:
     * 
     * Process the @Inheritance or inheritance tag metadata for a parent of
     * an inheritance hierarchy. One may or may not be specified for the entity 
     * class that is the root of the entity class hierarchy, so we need to
     * default in this case.
     */
    protected void processInheritance() {
        if (m_descriptor.ignoreInheritance()) {
            m_logger.logWarningMessage(m_logger.IGNORE_INHERITANCE, m_descriptor);
        } else {
            // Get the inheritance strategy and store it on the descriptor.
            String inheritanceStrategy = getInheritanceStrategy();
            if (inheritanceStrategy.equals("")) {
                inheritanceStrategy = InheritanceType.SINGLE_TABLE.name();
            }

            m_descriptor.setInheritanceStrategy(inheritanceStrategy);
                
            // Process the discriminator column metadata.
            processDiscriminatorColumn();
                
            // Process the discriminator value metadata.
            processDiscriminatorValue();
        }
    }
    
    /**
     * INTERNAL:
     * 
     * Process the inheritance metadata for an inheritance subclass. The
     * parent descriptor must be provided.
     */
    protected void processInheritanceSubclass(MetadataDescriptor parentDescriptor) {
        // Ignore any project.xml settings.
        if (m_descriptor.ignoreInheritance()) {
            // Log a warning that we are ignoring the inheritance metadata.
            m_logger.logWarningMessage(m_logger.IGNORE_INHERITANCE, m_descriptor);
        } else {
            // Inheritance.stategy() = SINGLE_TABLE, set the flag. Unless this
            // descriptor has its own inheritance.
            if (parentDescriptor.usesSingleTableInheritanceStrategy() && ! hasInheritance()) {
                m_descriptor.setSingleTableInheritanceStrategy();
            } else {
                // Inheritance.stategy() = JOINED, look for primary key join 
                // column(s) and add multiple table key fields.
                MetadataPrimaryKeyJoinColumns primaryKeyJoinColumns = getPrimaryKeyJoinColumns(m_descriptor.getPrimaryKeyTableName(), m_descriptor.getPrimaryTableName());
                addMultipleTableKeyFields(primaryKeyJoinColumns, m_logger.INHERITANCE_PK_COLUMN, m_logger.INHERITANCE_FK_COLUMN);
            }    
            
            // Process the discriminator value, unless this descriptor has
            // its own inheritance.
            if (! hasInheritance()) {
                processDiscriminatorValue();
            }
            
            // If the root descriptor has an id class, we need to set the same 
            // id class on our descriptor.
            if (parentDescriptor.hasCompositePrimaryKey()) {
                m_descriptor.setPKClass(parentDescriptor.getPKClassName());
            }
        }
    }
    
    /**
     * INTERNAL:
     * Process and array of @JoinColumn into a list of metadata join column.
     */
    protected List<MetadataJoinColumn> processJoinColumns(JoinColumn[] joinColumns) {
        ArrayList<MetadataJoinColumn> list = new ArrayList<MetadataJoinColumn>();
        
        for (JoinColumn joinColumn : joinColumns) {
            list.add(new MetadataJoinColumn(joinColumn));
        }
        
        return list;
    }
    
    /**
     * INTERNAL:
     * Process the XML lifecycle event for the given listener.
     */
    protected void processLifecycleEvent(MetadataEntityListener listener, Node node, String event, XMLHelper helper, Method[] candidateMethods) {
        Node eventNode = helper.getNode(node, event);
        
        if (eventNode != null) {
            String methodName = helper.getNodeValue(eventNode, XMLConstants.ATT_METHOD_NAME);
            Method method = MetadataHelper.getMethodForName(candidateMethods, methodName);

            if (method == null) {
                m_validator.throwInvalidCallbackMethod(listener.getListenerClass(), methodName);
            } else if (event.equals(XMLConstants.PRE_PERSIST)) {
                setPrePersist(method, listener);
            } else if (event.equals(XMLConstants.POST_PERSIST)) {
                setPostPersist(method, listener);
            } else if (event.equals(XMLConstants.PRE_REMOVE)) {
                setPreRemove(method, listener);
            } else if (event.equals(XMLConstants.POST_REMOVE)) {
                setPostRemove(method, listener);
            } else if (event.equals(XMLConstants.PRE_UPDATE)) {
                setPreUpdate(method, listener);
            } else if (event.equals(XMLConstants.POST_UPDATE)) {
                setPostUpdate(method, listener);
            } else if (event.equals(XMLConstants.POST_LOAD)) {
                setPostLoad(method, listener);
            }
        }
    }
    
    /**
     * INTERNAL:
     * Process the XML lifecycle events for the given listener.
     */
    protected void processLifecycleEvents(MetadataEntityListener listener, Node node, XMLHelper helper, Method[] candidateMethods) {
        processLifecycleEvent(listener, node, XMLConstants.PRE_PERSIST, helper, candidateMethods);
        processLifecycleEvent(listener, node, XMLConstants.POST_PERSIST, helper, candidateMethods);
        processLifecycleEvent(listener, node, XMLConstants.PRE_REMOVE, helper, candidateMethods);
        processLifecycleEvent(listener, node, XMLConstants.POST_REMOVE, helper, candidateMethods);
        processLifecycleEvent(listener, node, XMLConstants.PRE_UPDATE, helper, candidateMethods);
        processLifecycleEvent(listener, node, XMLConstants.POST_UPDATE, helper, candidateMethods);
        processLifecycleEvent(listener, node, XMLConstants.POST_LOAD, helper, candidateMethods);
    }
    
    /**
     * INTERNAL:
     * Process the listeners for this class.
     */
    public void processListeners(ClassLoader loader) {
        // Step 1 - process the default listeners.
        processDefaultListeners(loader);

        // Step 2 - process the entity listeners that are defined on the entity 
        // class and mapped superclasses (taking metadata-complete into 
        // consideration). Go through the mapped superclasses first, top -> down 
        // only if the exclude superclass listeners flag is not set.    
        if (! m_descriptor.excludeSuperclassListeners()) {
            List<ClassAccessor> mappedSuperclasses = getMappedSuperclasses();
            int mappedSuperclassesSize = mappedSuperclasses.size();
            
            for (int i = mappedSuperclassesSize - 1; i >= 0; i--) {
                mappedSuperclasses.get(i).processEntityListeners(getJavaClass(), loader);
            }
        }
        
        processEntityListeners(getJavaClass(), loader); 
                
        // Step 3 - process the entity class for lifecycle callback methods. Go
        // through the mapped superclasses as well.
        MetadataEntityListener listener = processEntityEventListener(loader);
        
        if (! m_descriptor.excludeSuperclassListeners()) {
            for (ClassAccessor mappedSuperclass : getMappedSuperclasses()) {
                mappedSuperclass.processMappedSuperclassEventListener(listener, getJavaClass(), loader);
            }
        }
        
        // Add the listener only if we actually found callback methods.
        if (listener.hasCallbackMethods()) {
            m_descriptor.setEntityEventListener(listener);
        }
    }

    /**
     * INTERNAL:
     * Process the @MappedSuperclass(es) if there are any. There may be
     * several MappedSuperclasses for any given Entity.
     */
    protected void processMappedSuperclass() {
        // Process the common class level attributes that an entity or
        // mapped superclass may define.
        processClassMetadata();
            
        // Process the accessors from the mapped superclass.
        processAccessors();
    }
    
    /**
     * INTERNAL:
     * Process the @MappedSuperclass(es) if there are any. There may be
     * several MappedSuperclasses for any given Entity.
     */
    protected void processMappedSuperclasses() {
        for (ClassAccessor mappedSuperclass : getMappedSuperclasses()) {
            mappedSuperclass.process();
        }
    }
    
    /**
     * INTERNAL: (Overridden in XMLCLassAccessor)
     * Process the mapped superclass class for lifecycle callback event methods.
     */
    public void processMappedSuperclassEventListener(MetadataEntityListener listener, Class entityClass, ClassLoader loader) {
        // Check the mapped superclass for lifecycle callback annotations.
        processCallbackMethods(MetadataHelper.getCandidateCallbackMethodsForMappedSuperclass(getJavaClass(), entityClass), listener);
    }
    
    /**
     * INTERNAL: (Overridden in XMLClassAccessor)
     * 
     * Process a @NamedNativeQueries. The method will also look for 
     * a @NamedNativeQuery. This method currently only stores the queries if 
     * there are some. The actually query processing isn't done till 
     * addNamedQueriesToSession is called.
     */
    protected void processNamedNativeQueries() {
        // Look for a @NamedNativeQueries.
        NamedNativeQueries namedNativeQueries = getAnnotation(NamedNativeQueries.class);
        if (namedNativeQueries != null) {
            for (NamedNativeQuery namedNativeQuery : namedNativeQueries.value()) {
                processNamedNativeQuery(new MetadataNamedNativeQuery(namedNativeQuery, getJavaClass()));
            }
        }
        
        // Look for a @NamedNativeQuery.
        NamedNativeQuery namedNativeQuery = getAnnotation(NamedNativeQuery.class);
        if (namedNativeQuery != null) {
            processNamedNativeQuery(new MetadataNamedNativeQuery(namedNativeQuery, getJavaClass()));
        }
    }
    
    /**
     * INTERNAL:
     * Process a MetadataNamedNativeQuery. The actually query processing isn't 
     * done till addNamedQueriesToSession is called.
     */
    protected void processNamedNativeQuery(MetadataNamedNativeQuery namedNativeQuery) {
        if (m_project.hasNamedNativeQuery(namedNativeQuery.getName())) {
            MetadataNamedNativeQuery existingNamedNativeQuery = m_project.getNamedNativeQuery(namedNativeQuery.getName());
            
            if (existingNamedNativeQuery.loadedFromAnnotations() && namedNativeQuery.loadedFromXML()) {
                // Override the existing query.
                m_project.addNamedNativeQuery(namedNativeQuery);
            } else {
                // Ignore the query and log a message.
                m_logger.logWarningMessage(namedNativeQuery.getIgnoreLogMessageContext(), namedNativeQuery.getLocation(), namedNativeQuery.getName());
            }
        } else {
            m_project.addNamedNativeQuery(namedNativeQuery);
        }
    }
    
    /**
     * INTERNAL: (Overridden in XMLClassAccessor)
     * 
     * Process a @NamedQueries. The method will also look for a @NamedQuery.
     * This method currently only stores the queries if there are some. The
     * actually query processing isn't done till addNamedQueriesToSession is
     * called.
     */
    protected void processNamedQueries() {
        // Look for a @NamedQueries.
        NamedQueries namedQueries = getAnnotation(NamedQueries.class);
        
        if (namedQueries != null) {
            for (NamedQuery namedQuery : namedQueries.value()) {
                processNamedQuery(new MetadataNamedQuery(namedQuery, getJavaClass()));
            }
        }
        
        // Look for a @NamedQuery.
        NamedQuery namedQuery = getAnnotation(NamedQuery.class);
        
        if (namedQuery != null) {
            processNamedQuery(new MetadataNamedQuery(namedQuery, getJavaClass()));
        }
    }
    
    /**
     * INTERNAL:
     * Add a metadata named query to the project. The actually query processing 
     * isn't done till addNamedQueriesToSession is called.
     */
    protected void processNamedQuery(MetadataNamedQuery namedQuery) {
        if (m_project.hasNamedQuery(namedQuery.getName())) {
            MetadataNamedQuery existingNamedQuery = m_project.getNamedQuery(namedQuery.getName());
            
            if (existingNamedQuery.loadedFromAnnotations() && namedQuery.loadedFromXML()) {
                // Override the existing query.
                m_project.addNamedQuery(namedQuery);
            } else {
                // Ignore the query and log a message.
                m_logger.logWarningMessage(namedQuery.getIgnoreLogMessageContext(), namedQuery.getLocation(), namedQuery.getName());
            }
        } else {
            m_project.addNamedQuery(namedQuery);
        }
    }
    
    /**
     * INTERNAL:
     * Process a MetadataSecondaryTable. Do all the table name defaulting and 
     * set the correct, fully qualified name on the TopLink DatabaseTable.
     */
    protected void processSecondaryTable(MetadataSecondaryTable secondaryTable) {
        // Process any table defaults and log warning messages.
        processTable(secondaryTable, secondaryTable.getName());
        
        // Add the table to the descriptor.
        m_descriptor.addTable(secondaryTable.getDatabaseTable());
        
        // Get the primary key join column(s) and add the multiple table key fields.
        MetadataPrimaryKeyJoinColumns primaryKeyJoinColumns = secondaryTable.getPrimaryKeyJoinColumns(m_descriptor.getPrimaryTableName());
        addMultipleTableKeyFields(primaryKeyJoinColumns, m_logger.SECONDARY_TABLE_PK_COLUMN, m_logger.SECONDARY_TABLE_FK_COLUMN);
    }
    
    /**
     * INTERNAL: (Overridden in XMLClassAccessor)
     * Process a @SecondaryTables. If one isn't found, try a @SecondaryTable.
     * WIP - If the @SecondaryTable does not define the pkJoinColumns(), we
     * could look for PrimaryKeyJoinColumns on the class itself. This is not
     * mandatory through.
     */
    protected void processSecondaryTables() {
        // Look for a SecondaryTables annotation.
        SecondaryTables secondaryTables = getAnnotation(SecondaryTables.class);
        if (secondaryTables != null) {
            if (m_descriptor.ignoreTables()) {
                m_logger.logWarningMessage(m_logger.IGNORE_SECONDARY_TABLE_ANNOTATION, getJavaClass());        
            } else {
                for (SecondaryTable secondaryTable : secondaryTables.value()) {
                    processSecondaryTable(new MetadataSecondaryTable(secondaryTable, m_logger));
                }
            }
        } else {
            // Look for a SecondaryTable annotation
            SecondaryTable secondaryTable = getAnnotation(SecondaryTable.class);
            if (secondaryTable != null) {
                if (m_descriptor.ignoreTables()) {
                    m_logger.logWarningMessage(m_logger.IGNORE_SECONDARY_TABLE_ANNOTATION, getJavaClass());        
                } else {    
                    processSecondaryTable(new MetadataSecondaryTable(secondaryTable, m_logger));
                }
            }
        }
    } 
    
    /**
     * INTERNAL:
     * Process an sql result set mapping metadata into a TopLink 
     * SqlResultSetMapping and store it on the session.
     */
    protected void processSqlResultSetMapping(MetadataSQLResultSetMapping sqlResultSetMapping) {        
        // Initialize a new SqlResultSetMapping (with the metadata name)
        oracle.toplink.essentials.queryframework.SQLResultSetMapping mapping = new oracle.toplink.essentials.queryframework.SQLResultSetMapping(sqlResultSetMapping.getName());
        
        // Process the entity results.
        for (MetadataEntityResult eResult : sqlResultSetMapping.getEntityResults()) {
            EntityResult entityResult = new EntityResult(eResult.getEntityClass().getName());
        
            // Process the field results.
            for (MetadataFieldResult fResult : eResult.getFieldResults()) {
                entityResult.addFieldResult(new FieldResult(fResult.getName(), fResult.getColumn()));
            }
        
            // Process the discriminator value;
            entityResult.setDiscriminatorColumn(eResult.getDiscriminatorColumn());
        
            // Add the result to the SqlResultSetMapping.
            mapping.addResult(entityResult);
        }
        
        // Process the column results.
        for (String columnResult : sqlResultSetMapping.getColumnResults()) {
            mapping.addResult(new ColumnResult(columnResult));
        }
            
        getProject().getSession().getProject().addSQLResultSetMapping(mapping);
    }
    
    /**
     * INTERNAL:
     * Process a @SqlResultSetMappings.
     */
    protected void processSqlResultSetMappings() {
        // Look for a @SqlResultSetMappings.
        SqlResultSetMappings sqlResultSetMappings = getAnnotation(SqlResultSetMappings.class);

        if (sqlResultSetMappings != null) {
            for (SqlResultSetMapping sqlResultSetMapping : sqlResultSetMappings.value()) {
                processSqlResultSetMapping(new MetadataSQLResultSetMapping(sqlResultSetMapping));
            }
        } else {
            // Look for a @SqlResultSetMapping.
            SqlResultSetMapping sqlResultSetMapping = getAnnotation(SqlResultSetMapping.class);
            
            if (sqlResultSetMapping != null) {
                processSqlResultSetMapping(new MetadataSQLResultSetMapping(sqlResultSetMapping));
            }
        }
    } 

    /**
     * INTERNAL: (Overridden in XMLClassAccessor)
	 * Process a @Table annotation.
	 */
	protected void processTable() {
        if (m_descriptor.ignoreTables()) {
            m_logger.logWarningMessage(m_logger.IGNORE_TABLE_ANNOTATION, getJavaClass());
        } else {
            Table table = getAnnotation(Table.class);
            processTable(new MetadataTable(table, m_logger));
        }
    }
    
    /**
     * INTERNAL:
     * 
	 * Process a MetadataTable. Do all the table name defaulting and set the
     * correct, fully qualified name on the TopLink DatabaseTable.
	 */
    protected void processTable(MetadataTable table) {
        // Process any table defaults and log warning messages.
        processTable(table, m_descriptor.getDefaultTableName());

        // Set the table on the descriptor.
        m_descriptor.setPrimaryTable(table.getDatabaseTable());
    }
    
    /**
     * INTERNAL:
     * 
     * Process any inheritance specifics. This method will fast track any 
     * parent inheritance processing, be it specified or defaulted.
     */
    protected void processTableAndInheritance() {
        // If we are an inheritance subclass, ensure our parent is processed 
        // first since it has information its subclasses depend on.
		if (isInheritanceSubclass()) {
            MetadataDescriptor parentDescriptor = getInheritanceParentDescriptor();
            
            // Process the parent class accesor if it hasn't already been done.
            ClassAccessor parentAccessor = parentDescriptor.getClassAccessor();
            if (parentAccessor == null) {
                parentAccessor = processAccessor(parentDescriptor);
            }
            
            // A parent, who didn't know they were a parent (a root class of an 
            // inheritance hierarchy that does not have an  @Inheritance 
            // annotation or XML tag) must process and default the inheritance 
            // parent metadata.
            if (! parentDescriptor.hasInheritance()) {
                parentAccessor.processInheritance();
            }
                
            // If this entity has inheritance metadata as well, then the 
            // inheritance stragety is mixed and we need to process the 
            // inheritance parent metadata for this entity's subclasses to 
            // process correctly.
            // WIP - check that that strategies are indeed changing ....
            if (hasInheritance()) {
                // Process the table metadata if there is one, otherwise default.
                processTable();
                
                // Process the parent inheritance specifics.
                processInheritance();
                
                // Process the inheritance subclass metadata.
                processInheritanceSubclass(parentDescriptor);
            } else {
                // Process the table information for this descriptor (for a 
                // joined strategy), if there is one specified. Must be called
                // before processing the inheritance metadata.
                if (parentDescriptor.usesJoinedInheritanceStrategy()) {
                    processTable();
                }
                
                // Process the inheritance subclass metadata.
                processInheritanceSubclass(parentDescriptor);
            }
		} else {
            // Process the table metadata if there is one, otherwise default.
            processTable();
            
            // If we have inheritance metadata, then process it now. If we are 
            // an inheritance root class that doesn't know it, a subclass will 
            // force this processing to occur.
            if (hasInheritance()) {
                processInheritance();
            }
        }
    }
    
    /**
     * INTERNAL:
     * Set the post load event method on the listener.
     */
    protected void setPostLoad(Method method, MetadataEntityListener listener) {
        listener.setPostBuildMethod(method);
        listener.setPostCloneMethod(method);
        listener.setPostRefreshMethod(method);
    }
    
    /**
     * INTERNAL:
     * Set the post persist event method on the listener.
     */
    protected void setPostPersist(Method method, MetadataEntityListener listener) {
        listener.setPostInsertMethod(method); 
    }
    
    /**
     * INTERNAL:
     * Set the post remove event method on the listener.
     */
    protected void setPostRemove(Method method,  MetadataEntityListener listener) {
        listener.setPostDeleteMethod(method);
    }
    
    /**
     * INTERNAL:
     * * Set the post update event method on the listener.
     */
    protected void setPostUpdate(Method method,  MetadataEntityListener listener) {
        listener.setPostUpdateMethod(method);
    }
            
    /**
     * INTERNAL:
     * Set the pre persist event method on the listener.
     */
    protected void setPrePersist(Method method,  MetadataEntityListener listener) {
        listener.setPrePersistMethod(method);
    }
    
    /**
     * INTERNAL:
     * Set the pre remove event method on the listener.
     */
    protected void setPreRemove(Method method,  MetadataEntityListener listener) {
        listener.setPreRemoveMethod(method);
    }
    
    /**
     * INTERNAL:
     * Set the pre update event method on the listener.
     */
    protected void setPreUpdate(Method method,  MetadataEntityListener listener) {
        listener.setPreUpdateWithChangesMethod(method);
    }
    
    /**
     * INTERNAL:
     * 
     * Call this method after a primary key should have been found.
     */
    protected void validatePrimaryKey() {
        // If this descriptor has a composite primary key, check that all 
        // our composite primary key attributes were validated. 
        if (m_descriptor.hasCompositePrimaryKey()) {
            if (m_descriptor.pkClassWasNotValidated()) {
                m_validator.throwInvalidCompositePKSpecification(getJavaClass(), m_descriptor.getPKClassName());
            }
        } else {
            // Descriptor has a single primary key. Validate an id 
            // attribute was found, unless we are an inheritance subclass
            // or an aggregate descriptor.
            if (! m_descriptor.hasPrimaryKeyFields() && ! isInheritanceSubclass()) {
                m_validator.throwNoPrimaryKeyAnnotationsFound(getJavaClass());
            }
        }  
    }
}