/*******************************************************************************
 * Copyright (c) 1998, 2008 Oracle. 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
 ******************************************************************************/
package org.eclipse.persistence.oxm;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.Collection;
import javax.xml.namespace.QName;

import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.XMLMarshalException;
import org.eclipse.persistence.internal.oxm.accessor.OrmAttributeAccessor;
import org.eclipse.persistence.internal.oxm.XPathFragment;
import org.eclipse.persistence.internal.oxm.documentpreservation.DescriptorLevelDocumentPreservationPolicy;
import org.eclipse.persistence.internal.oxm.documentpreservation.NoDocumentPreservationPolicy;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.mappings.AttributeAccessor;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.OneToOneMapping;
import org.eclipse.persistence.oxm.documentpreservation.DocumentPreservationPolicy;
import org.eclipse.persistence.oxm.mappings.XMLCompositeCollectionMapping;
import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping;
import org.eclipse.persistence.oxm.platform.SAXPlatform;
import org.eclipse.persistence.oxm.platform.XMLPlatform;
import org.eclipse.persistence.oxm.schema.XMLSchemaReference;
import org.eclipse.persistence.sessions.DatabaseSession;
import org.eclipse.persistence.sessions.Project;
import org.eclipse.persistence.sessions.SessionEventListener;
import org.eclipse.persistence.sessions.factories.SessionManager;
import org.eclipse.persistence.sessions.factories.XMLSessionConfigLoader;

/**
 * <p>
 * An XMLContext is created based on EclipseLink sessions or projects and can then
 * used to create instances of XMLMarshaller, XMLUnmarshaller and XMLValidator.
 *
 * <p>
 * There are constructors to create an XMLContext with a single EclipseLink project
 * or with a String which is a single EclipseLink session name or a ':' separated
 * list of EclipseLink session names.
 *
 * <p>
 * <em>Code Sample</em><br>
 * <code>
 *  XMLContext context = new XMLContext("mySessionName");<br>
 *  XMLMarshaller marshaller = context.createMarshaller();<br>
 *  XMLUnmarshaller unmarshaller = context.createUnmarshaller();<br>
 *  XMLValidator validator = context.createValidator();<br>
 *  <code>
 *
 *  <p>The XMLContext is thread-safe.  If multiple threads accessing the same XMLContext object
 *  request an XMLMarshaller, each will receive their own instance of XMLMarshaller, so any
 *  state that the XMLMarshaller maintains will be unique to that process.  The same is true
 *  of instances of XMLUnmarshaller and XMLValidator.
 *
 *  @see org.eclipse.persistence.oxm.XMLMarshaller
 *  @see org.eclipse.persistence.oxm.XMLUnmarshaller
 *  @see org.eclipse.persistence.oxm.XMLValidator
 *
 */
public class XMLContext {
    private List sessions;
    private Map descriptorsByQName;
    private Map descriptorsByGlobalType;
    private boolean hasDocumentPreservation = false;

    /**
     * Create a new XMLContext based on the specified session name or list of
     * session names
     *
     * @param sessionNames
     *            A single session name or multiple session names separated by a :
     */
    public XMLContext(String sessionNames) {
        this(sessionNames, PrivilegedAccessHelper.privilegedGetClassLoaderForClass(XMLContext.class));
    }

    /**
     * Create a new XMLContext based on the specified session name or list of
     * session names
     *
     * @param sessionNames
     *            A single session name or multiple session names separated by a :
     * @param classLoader
     *            classloader for loading sessions.xml
     */
    public XMLContext(String sessionNames, ClassLoader classLoader) {
        this(sessionNames, classLoader, null);
    }

    /**
     * Create a new XMLContext based on passed in session names and session meta
     * XML.
     *
     * @param sessionNames
     *            A single session name or multiple session names separated by
     *            a:
     * @param xmlResource
     *            path to XML file containing session meta data to initialize
     *            and load sessions.
     */
    public XMLContext(String sessionNames, String xmlResource) {
        this(sessionNames, PrivilegedAccessHelper.privilegedGetClassLoaderForClass(XMLContext.class), xmlResource);
    }

    /**
     * Create a new XMLContext based on passed in session names, classloader and
     * session meta XML.
     *
     * @param sessionNames
     *            A single session name or multiple session names separated by a :
     * @param classLoader
     *            classloader for loading sessions.xml
     * @param xmlResource
     *            path to XML file containing session meta data to initialize
     *            and load sessions.
     */
    public XMLContext(String sessionNames, ClassLoader classLoader, String xmlResource) {
        XMLSessionConfigLoader loader = null;
        if (xmlResource != null) {
            loader = new XMLSessionConfigLoader(xmlResource);
        } else {
            loader = new XMLSessionConfigLoader();
        }
        descriptorsByQName = new HashMap();
        descriptorsByGlobalType = new HashMap();
        StringTokenizer st = new StringTokenizer(sessionNames, ":");
        sessions = new ArrayList(st.countTokens());
        int index = 0;
        while (st.hasMoreTokens()) {
            sessions.add(buildSession(st.nextToken(), classLoader, loader));
            index++;
        }
        for (int x = index - 1; x >= 0; x--) {
            storeXMLDescriptorsByQName((DatabaseSession) sessions.get(x));
        }
    }

    /**
     * Create a new XMLContext based on the specified project
     *
     * @param project
     *            An EclipseLink project
     */
    public XMLContext(Project project) {
        this(project, Thread.currentThread().getContextClassLoader());
    }

    public XMLContext(Project project, ClassLoader classLoader) {
        if ((project.getDatasourceLogin() == null) || !(project.getDatasourceLogin().getDatasourcePlatform() instanceof XMLPlatform)) {
            XMLPlatform platform = new SAXPlatform();
            platform.getConversionManager().setLoader(classLoader);
            project.setLogin(new XMLLogin(platform));
        }
        sessions = new ArrayList(1);
        DatabaseSession session = project.createDatabaseSession();

        // turn logging for this session off and leave the global session up
        // Note: setting level to SEVERE or WARNING will printout stacktraces for expected exceptions
        session.setLogLevel(SessionLog.OFF);
        // dont turn off global static logging
        //AbstractSessionLog.getLog().log(AbstractSessionLog.INFO, "ox_turn_global_logging_off", getClass());        			
        //AbstractSessionLog.getLog().setLevel(AbstractSessionLog.OFF);
        setupDocumentPreservationPolicy(session);
        session.login();
        sessions.add(session);
        descriptorsByQName = new HashMap();
        descriptorsByGlobalType = new HashMap();
        storeXMLDescriptorsByQName(session);
    }

    public XMLContext(Collection projects) {
    	this(projects, Thread.currentThread().getContextClassLoader());
    }
    
    public XMLContext(Collection projects, ClassLoader classLoader) {
    	Iterator iterator = projects.iterator();
    	sessions = new ArrayList(projects.size());
		descriptorsByQName = new HashMap();
		descriptorsByGlobalType = new HashMap();
    	while(iterator.hasNext()) {
    		Project project = (Project)iterator.next();
    		if ((project.getDatasourceLogin() == null) || !(project.getDatasourceLogin().getDatasourcePlatform() instanceof XMLPlatform)) {
    			XMLPlatform platform = new SAXPlatform();
    			platform.getConversionManager().setLoader(classLoader);
    			project.setLogin(new XMLLogin(platform));
    		}
    		DatabaseSession session = project.createDatabaseSession();

    		// turn logging for this session off and leave the global session up
    		// Note: setting level to SEVERE or WARNING will printout stacktraces for expected exceptions
    		session.setLogLevel(SessionLog.OFF);
    		// dont turn off global static logging
    		//AbstractSessionLog.getLog().log(AbstractSessionLog.INFO, "ox_turn_global_logging_off", getClass());        			
    		//AbstractSessionLog.getLog().setLevel(AbstractSessionLog.OFF);
    		setupDocumentPreservationPolicy(session);
    		session.login();
    		sessions.add(session);
    		storeXMLDescriptorsByQName(session);
    	}
    }
    
    private DatabaseSession buildSession(String sessionName, ClassLoader classLoader, XMLSessionConfigLoader sessionLoader) throws XMLMarshalException {
        DatabaseSession dbSession;
        if (classLoader != null) {
            dbSession = (DatabaseSession) SessionManager.getManager().getSession(sessionLoader, sessionName, classLoader, false, true);
        } else {
            dbSession = (DatabaseSession) SessionManager.getManager().getSession(sessionLoader, sessionName, PrivilegedAccessHelper.privilegedGetClassLoaderForClass(this.getClass()), false, false, false);
        }
        if ((dbSession.getDatasourceLogin() == null) || !(dbSession.getDatasourceLogin().getDatasourcePlatform() instanceof XMLPlatform)) {
            XMLPlatform platform = new SAXPlatform();
            dbSession.setLogin(new XMLLogin(platform));
        }
        DatabaseSession session = dbSession.getProject().createDatabaseSession();
        if (dbSession.getEventManager().hasListeners()) {
            List listeners = dbSession.getEventManager().getListeners();
            int listenersSize = listeners.size();
            for (int x = 0; x < listenersSize; x++) {
                session.getEventManager().addListener((SessionEventListener) listeners.get(x));
            }
        }
        session.setExceptionHandler(dbSession.getExceptionHandler());
        session.setLogLevel(SessionLog.OFF);
        setupDocumentPreservationPolicy(session);
        session.login();
        return session;
    }

    /**
     * INTERNAL: Add and initialize a new session to the list of sessions
     * associated with this XMLContext.
     */
    public void addSession(DatabaseSession sessionToAdd) {
        if ((sessionToAdd.getDatasourceLogin() == null) || !(sessionToAdd.getDatasourceLogin().getDatasourcePlatform() instanceof XMLPlatform)) {
            XMLPlatform platform = new SAXPlatform();
            sessionToAdd.setLogin(new XMLLogin(platform));
        }
        DatabaseSession session = sessionToAdd.getProject().createDatabaseSession();
        if (sessionToAdd.getEventManager().hasListeners()) {
            List listeners = sessionToAdd.getEventManager().getListeners();
            int listenersSize = listeners.size();
            for (int x = 0; x < listenersSize; x++) {
                session.getEventManager().addListener((SessionEventListener) listeners.get(x));
            }
        }
        session.setExceptionHandler(sessionToAdd.getExceptionHandler());
        session.setLogLevel(SessionLog.OFF);
        this.setupDocumentPreservationPolicy(session);
        session.login();
        sessions.add(session);

        storeXMLDescriptorsByQName(session);

    }

    /**
     * Create a new XMLUnmarshaller
     *
     * @return An XMLUnmarshaller based on this XMLContext
     */
    public XMLUnmarshaller createUnmarshaller() {
        XMLUnmarshaller unmarshaller = new XMLUnmarshaller(this);
        return unmarshaller;
    }

    /**
     * Create a new XMLBinder
     * @return an XMLBinder based on this XMLContext
     */
    public XMLBinder createBinder() {
        return new XMLBinder(this);
    }

    /**
     * Create a new XMLMarshaller
     *
     * @return An XMLMarshaller based on this XMLContext
     */
    public XMLMarshaller createMarshaller() {
        XMLMarshaller marshaller = new XMLMarshaller(this);
        return marshaller;
    }

    /**
     * Create a new XMLValidator
     *
     * @return An XMLValidator based on this XMLContext
     */
    public XMLValidator createValidator() {
        XMLValidator validator = new XMLValidator(this);
        return validator;
    }

    /**
     * INTERNAL: Return the session corresponding to this object. Since the
     * object may be mapped by more that one of the projects used to create the
     * XML Context, this method will return the first match.
     *
     * The session will be a unit of work if document preservation is not
     * enabled.  This method will typically  be used for unmarshalling
     * when a non-shared cache is desired.
     */
    public AbstractSession getReadSession(Object object) {
        if (null == object) {
            return null;
        }
        int numberOfSessions = sessions.size();
        for (int x = 0; x < numberOfSessions; x++) {
            AbstractSession next = ((AbstractSession) sessions.get(x));
            XMLDescriptor xmlDescriptor = (XMLDescriptor) next.getDescriptor(object);
            if (xmlDescriptor != null) {
                // we don't currently support document preservation
                // and non-shared cache (via unit of work)
                //if (!documentPreservationPolicy.shouldPreserveDocument()) {
                next = next.acquireUnitOfWork();
                //}
                return next;
            }
        }
        throw XMLMarshalException.descriptorNotFoundInProject(object.getClass().getName());
    }

    /**
     * INTERNAL:
     * Return the session corresponding to this class. Since the class
     * may be mapped by more that one of the projects used to create the XML
     * Context, this method will return the first match.
     *
     * The session will be a unit of work if document preservation is not
     * enabled.  This method will typically  be used for unmarshalling
     * when a non-shared cache is desired.
     */
    public AbstractSession getReadSession(Class clazz) {
        if (null == clazz) {
            return null;
        }
        int numberOfSessions = sessions.size();
        for (int x = 0; x < numberOfSessions; x++) {
            AbstractSession next = ((AbstractSession) sessions.get(x));
            XMLDescriptor xmlDescriptor = (XMLDescriptor) next.getDescriptor(clazz);
            if (xmlDescriptor != null) {
                // we don't currently support document preservation
                // and non-shared cache (via unit of work)
                //if (!documentPreservationPolicy.shouldPreserveDocument()) {
                next = next.acquireUnitOfWork();
                //}
                return next;
            }
        }
        throw XMLMarshalException.descriptorNotFoundInProject(clazz.getName());
    }

    /**
     * INTERNAL:
     * Return the session corresponding to this XMLDescriptor. Since
     * the class may be mapped by more that one of the projects used to create
     * the XML Context, this method will return the first match.
     *
     * The session will be a unit of work if document preservation is not
     * enabled.  This method will typically  be used for unmarshalling
     * when a non-shared cache is desired.
     */
    public AbstractSession getReadSession(XMLDescriptor xmlDescriptor) {
        if (null == xmlDescriptor) {
            return null;
        }
        int numberOfSessions = sessions.size();
        for (int x = 0; x < numberOfSessions; x++) {
            AbstractSession next = ((AbstractSession) sessions.get(x));
            if (next.getProject().getOrderedDescriptors().contains(xmlDescriptor)) {
                // we don't currently support document preservation
                // and non-shared cache (via unit of work)
                //if (!documentPreservationPolicy.shouldPreserveDocument()) {
                next = next.acquireUnitOfWork();
                //}
                return next;
            }
        }
        throw XMLMarshalException.descriptorNotFoundInProject(xmlDescriptor.getJavaClass().getName());
    }

    /**
     * INTERNAL: Return the EclipseLink session used to marshall.
     */
    public List getSessions() {
        return sessions;
    }

    /**
     * INTERNAL: <code>
     * XMLContext xmlContext = new XMLContext("path0:path1");<br>
     * DatabaseSession session = xmlContext.getSession(0);  // returns session for path0<br>
     * </code>
     */
    public DatabaseSession getSession(int index) {
        if (null == sessions) {
            return null;
        }
        return (DatabaseSession) sessions.get(index);
    }

    /**
     * INTERNAL: Return the session corresponding to this object. Since the
     * object may be mapped by more that one of the projects used to create the
     * XML Context, this method will return the first match.
     */
    public AbstractSession getSession(Object object) {
    	if (null == object) {
            return null;
        }
        int numberOfSessions = sessions.size();
        for (int x = 0; x < numberOfSessions; x++) {
            AbstractSession next = ((AbstractSession) sessions.get(x));
            if (next.getDescriptor(object) != null) {
                return next;
            }
        }
        throw XMLMarshalException.descriptorNotFoundInProject(object.getClass().getName());
    }

    /**
     * INTERNAL: Return the session corresponding to this class. Since the class
     * may be mapped by more that one of the projects used to create the XML
     * Context, this method will return the first match.
     */
    public AbstractSession getSession(Class clazz) {
        if (null == clazz) {
            return null;
        }
        int numberOfSessions = sessions.size();
        for (int x = 0; x < numberOfSessions; x++) {
            AbstractSession next = ((AbstractSession) sessions.get(x));
            if (next.getDescriptor(clazz) != null) {
                return next;
            }
        }
        throw XMLMarshalException.descriptorNotFoundInProject(clazz.getName());
    }

    /**
     * INTERNAL: Return the session corresponding to this XMLDescriptor. Since
     * the class may be mapped by more that one of the projects used to create
     * the XML Context, this method will return the first match.
     */
    public AbstractSession getSession(XMLDescriptor xmlDescriptor) {
        if (null == xmlDescriptor) {
            return null;
        }
        int numberOfSessions = sessions.size();
        for (int x = 0; x < numberOfSessions; x++) {
            AbstractSession next = ((AbstractSession) sessions.get(x));
            if (next.getProject().getOrderedDescriptors().contains(xmlDescriptor)) {
                return next;
            }
        }
        throw XMLMarshalException.descriptorNotFoundInProject(xmlDescriptor.getJavaClass().getName());
    }

    private void storeXMLDescriptorsByQName(DatabaseSession session) {
        Iterator iterator = session.getProject().getOrderedDescriptors().iterator();
        while (iterator.hasNext()) {
            XMLDescriptor xmlDescriptor = (XMLDescriptor) iterator.next();
            storeXMLDescriptorByQName(xmlDescriptor);
        }
    }

    /**
     * INTERNAL:
     */
    public void storeXMLDescriptorByQName(XMLDescriptor xmlDescriptor) {
        QName descriptorQName;
        String defaultRootName;

        Vector tableNames = xmlDescriptor.getTableNames();
        for (int i = 0; i < tableNames.size(); i++) {
            defaultRootName = (String) tableNames.get(i);

            if (null != defaultRootName) {
                int index = defaultRootName.indexOf(':');
                String defaultRootLocalName = defaultRootName.substring(index + 1);
                if (index > -1) {
                    String defaultRootPrefix = defaultRootName.substring(0, index);
                    String defaultRootNamespaceURI = xmlDescriptor.getNamespaceResolver().resolveNamespacePrefix(defaultRootPrefix);
                    descriptorQName = new QName(defaultRootNamespaceURI, defaultRootLocalName);
                } else {
                    if(xmlDescriptor.getNamespaceResolver() != null) {
                        descriptorQName = new QName(xmlDescriptor.getNamespaceResolver().getDefaultNamespaceURI(), defaultRootLocalName);
                    } else {
                        descriptorQName = new QName(defaultRootLocalName);
                    }
                }
                if (!xmlDescriptor.hasInheritance() || xmlDescriptor.getInheritancePolicy().isRootParentDescriptor()) {
                    descriptorsByQName.put(descriptorQName, xmlDescriptor);
                } else {
                    //this means we have a descriptor that is a child in an inheritance hierarchy
                    XMLDescriptor existingDescriptor = (XMLDescriptor) descriptorsByQName.get(descriptorQName);
                    if (existingDescriptor == null) {
                        descriptorsByQName.put(descriptorQName, xmlDescriptor);
                    }
                }
            }
        }

        XMLSchemaReference xmlSchemaReference = xmlDescriptor.getSchemaReference();
        if (null != xmlSchemaReference) {
            String schemaContext = xmlSchemaReference.getSchemaContext();
            if ((xmlSchemaReference.getType() == XMLSchemaReference.COMPLEX_TYPE) || (xmlSchemaReference.getType() == XMLSchemaReference.SIMPLE_TYPE)) {
                if ((null != schemaContext) && (schemaContext.lastIndexOf('/') == 0)) {
                    schemaContext = schemaContext.substring(1, schemaContext.length());
                    XPathFragment typeFragment = new XPathFragment(schemaContext);
                    if (null != xmlDescriptor.getNamespaceResolver()) {
                        typeFragment.setNamespaceURI(xmlDescriptor.getNamespaceResolver().resolveNamespacePrefix(typeFragment.getPrefix()));
                    }
                    this.descriptorsByGlobalType.put(typeFragment, xmlDescriptor);
                } else {
                    QName qname = xmlSchemaReference.getSchemaContextAsQName();
                    if (qname != null) {
                        if (xmlDescriptor.isWrapper() && xmlDescriptor.getJavaClassName().contains("ObjectWrapper")) {
                            return;
                        }
                        XPathFragment typeFragment = new XPathFragment();
                        typeFragment.setLocalName(qname.getLocalPart());
                        typeFragment.setNamespaceURI(qname.getNamespaceURI());
                        this.descriptorsByGlobalType.put(typeFragment, xmlDescriptor);
                    }
                }
            }
        }
    }

    /**
     * INTERNAL: Return the XMLDescriptor with the default root mapping matchin
     * the QName paramater.
     */
    public XMLDescriptor getDescriptor(QName qName) {
        return (XMLDescriptor) descriptorsByQName.get(qName);
    }

    public void addDescriptorByQName(QName qName, XMLDescriptor descriptor) {
    	descriptorsByQName.put(qName, descriptor);
    }
    
    /**
     * INTERNAL: Return the XMLDescriptor mapped to the global type matching the
     * XPathFragment parameter.
     */
    public XMLDescriptor getDescriptorByGlobalType(XPathFragment xPathFragment) {
        return (XMLDescriptor) this.descriptorsByGlobalType.get(xPathFragment);
    }

    /**
     * INTERNAL:
     * Return the DocumentPreservationPolicy associated with this session
     * @param session
     * @return
     */
    public DocumentPreservationPolicy getDocumentPreservationPolicy(AbstractSession session) {
        XMLLogin login = (XMLLogin) session.getDatasourceLogin();
        return login.getDocumentPreservationPolicy();
    }

    public void setupDocumentPreservationPolicy(DatabaseSession session) {
        XMLLogin login = (XMLLogin) session.getDatasourceLogin();
        if (login.getDocumentPreservationPolicy() == null) {
            Iterator iterator = session.getProject().getOrderedDescriptors().iterator();
            while (iterator.hasNext()) {
                XMLDescriptor xmlDescriptor = (XMLDescriptor) iterator.next();
                if (xmlDescriptor.shouldPreserveDocument()) {
                    login.setDocumentPreservationPolicy(new DescriptorLevelDocumentPreservationPolicy());
                    break;
                }
            }
        }
        if (login.getDocumentPreservationPolicy() == null) {
            login.setDocumentPreservationPolicy(new NoDocumentPreservationPolicy());
        }
        
        login.getDocumentPreservationPolicy().initialize(this);

        if (login.getDocumentPreservationPolicy().shouldPreserveDocument() && !hasDocumentPreservation) {
            hasDocumentPreservation = true;
        }
    }

    /**
     * INTERNAL:
     * Return true if any session held onto by this context has a document preservation
     * policy that requires unmarshalling from a Node.
     */
    public boolean hasDocumentPreservation() {
        return this.hasDocumentPreservation;
    }
    
    /**
    * ADVANCED:
    * Adjust the OXM metadata to take into accound ORM mapping metadata,
    */
    public void applyORMMetadata(AbstractSession ormSession) {
        //Iterate over the ORM descriptors and check for matching OXM descriptors
        Iterator ormDescriptors = ormSession.getDescriptors().values().iterator();
        while(ormDescriptors.hasNext()) {
            ClassDescriptor ormDescriptor = (ClassDescriptor)ormDescriptors.next();
            Class javaClass = ormDescriptor.getJavaClass();
            AbstractSession oxmSession = null;
            try {
                oxmSession = this.getSession(javaClass);
            } catch(XMLMarshalException ex) {
                //if we couldn't find a session for this class, we
                //don't have an OX descriptor for it. 
            }
            if(oxmSession != null) {
                ClassDescriptor oxmDescriptor = oxmSession.getDescriptor(javaClass);
                //If we have an oxmDescriptor for this ORM descriptor, iterate over
                //mappings, and update the required OXM mappings attribute accessors
                Iterator<DatabaseMapping> ormMappings = ormDescriptor.getMappings().iterator();
                while(ormMappings.hasNext()) {
                    DatabaseMapping ormMapping = ormMappings.next();
                    DatabaseMapping oxmMapping = oxmDescriptor.getMappingForAttributeName(ormMapping.getAttributeName());
                    if(oxmMapping != null) {
                        AttributeAccessor oxmAccessor = oxmMapping.getAttributeAccessor();
                        OrmAttributeAccessor newAccessor = new OrmAttributeAccessor(ormMapping.getAttributeAccessor(), oxmAccessor);
                        if(ormMapping.isOneToOneMapping() && ((OneToOneMapping)ormMapping).usesIndirection()) {
                            newAccessor.setValueHolderProperty(true);
                        }
                        newAccessor.setChangeTracking(ormDescriptor.getObjectChangePolicy().isAttributeChangeTrackingPolicy());
                        oxmMapping.setAttributeAccessor(newAccessor);
                        
                        //check to see if we need to deal with containerAccessor
                        AttributeAccessor containerAccessor = null;
                        Class containerClass = null;
                        if(oxmMapping instanceof XMLCompositeObjectMapping) {
                            containerAccessor = ((XMLCompositeObjectMapping)oxmMapping).getContainerAccessor();
                            containerClass = ((XMLCompositeObjectMapping)oxmMapping).getReferenceClass();
                        } else if(oxmMapping instanceof XMLCompositeCollectionMapping) {
                            containerAccessor = ((XMLCompositeCollectionMapping)oxmMapping).getContainerAccessor();
                            containerClass = ((XMLCompositeCollectionMapping)oxmMapping).getReferenceClass();
                        }
                        if(containerAccessor != null) {
                            ClassDescriptor containerDescriptor = ormSession.getDescriptor(containerClass);
                            if(containerDescriptor != null) {
                                DatabaseMapping ormContainerMapping = containerDescriptor.getMappingForAttributeName(containerAccessor.getAttributeName());
                                if(ormContainerMapping != null) {
                                    //Check for indirection on the container mapping
                                    OrmAttributeAccessor ormAccessor = new OrmAttributeAccessor(ormContainerMapping.getAttributeAccessor(), containerAccessor);
                                    ormAccessor.setChangeTracking(containerDescriptor.getObjectChangePolicy().isAttributeChangeTrackingPolicy());
                                    ormAccessor.setValueHolderProperty(ormContainerMapping instanceof OneToOneMapping && ((OneToOneMapping)ormContainerMapping).usesIndirection());
                                    if(oxmMapping instanceof XMLCompositeObjectMapping) {
                                        ((XMLCompositeObjectMapping)oxmMapping).setContainerAccessor(ormAccessor);
                                    } else if(oxmMapping instanceof XMLCompositeCollectionMapping) {
                                        ((XMLCompositeCollectionMapping)oxmMapping).setContainerAccessor(ormAccessor);
                                    }
                                    
                                }
                            }
                        }
                    }
                }
            }
        }
    } 
}