/******************************************************************************* | |
* Copyright (c) 1998, 2013 Oracle and/or its affiliates. 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.Collection; | |
import java.util.Collections; | |
import java.util.HashMap; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.StringTokenizer; | |
import javax.xml.namespace.QName; | |
import org.eclipse.persistence.core.mappings.CoreAttributeAccessor; | |
import org.eclipse.persistence.descriptors.ClassDescriptor; | |
import org.eclipse.persistence.exceptions.XMLMarshalException; | |
import org.eclipse.persistence.internal.oxm.Context; | |
import org.eclipse.persistence.internal.oxm.XPathFragment; | |
import org.eclipse.persistence.internal.oxm.XPathQName; | |
import org.eclipse.persistence.internal.oxm.accessor.OrmAttributeAccessor; | |
import org.eclipse.persistence.internal.oxm.documentpreservation.DescriptorLevelDocumentPreservationPolicy; | |
import org.eclipse.persistence.internal.oxm.documentpreservation.NoDocumentPreservationPolicy; | |
import org.eclipse.persistence.internal.oxm.mappings.CompositeCollectionMapping; | |
import org.eclipse.persistence.internal.oxm.mappings.CompositeObjectMapping; | |
import org.eclipse.persistence.internal.oxm.mappings.Descriptor; | |
import org.eclipse.persistence.internal.oxm.mappings.Mapping; | |
import org.eclipse.persistence.internal.oxm.mappings.ObjectReferenceMapping; | |
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.CollectionMapping; | |
import org.eclipse.persistence.mappings.DatabaseMapping; | |
import org.eclipse.persistence.mappings.OneToOneMapping; | |
import org.eclipse.persistence.oxm.documentpreservation.DocumentPreservationPolicy; | |
import org.eclipse.persistence.oxm.platform.SAXPlatform; | |
import org.eclipse.persistence.oxm.platform.XMLPlatform; | |
import org.eclipse.persistence.sessions.DatabaseSession; | |
import org.eclipse.persistence.sessions.Project; | |
import org.eclipse.persistence.sessions.SessionEventListener; | |
import org.eclipse.persistence.sessions.SessionEventManager; | |
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 extends Context<AbstractSession, XMLDescriptor, XMLField, NamespaceResolver, Project, DatabaseSession, SessionEventListener> { | |
XMLContext(XMLContextState xmlContextState) { | |
this.contextState = xmlContextState; | |
} | |
/** | |
* 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) { | |
contextState = new XMLContextState(this, sessionNames, classLoader, xmlResource); | |
} | |
/** | |
* Create a new XMLContext based on the specified project | |
* | |
* @param project | |
* An EclipseLink project | |
*/ | |
public XMLContext(Project project) { | |
this(project, Thread.currentThread().getContextClassLoader()); | |
} | |
/** | |
* Create a new XMLContext based on the specified Project and ClassLoader. | |
* | |
* @param project An EclipseLink project | |
* @param classLoader The ClassLoader to be used | |
*/ | |
public XMLContext(Project project, ClassLoader classLoader) { | |
this (project, classLoader, (Collection<SessionEventListener>) null); | |
} | |
/** | |
* Create a new XMLContext based on the specified Project and ClassLoader. | |
* | |
* @param project An EclipseLink project | |
* @param classLoader The ClassLoader to be used | |
* @param sessionEventListener If non-null, this listener will be registered with the SessionEventManager | |
* @see SessionEventListener | |
* @see SessionEventManager | |
*/ | |
public XMLContext(Project project, ClassLoader classLoader, SessionEventListener sessionEventListener) { | |
Collection<SessionEventListener> sessionEventListeners = new ArrayList<SessionEventListener>(1); | |
sessionEventListeners.add(sessionEventListener); | |
contextState = new XMLContextState(this, project, classLoader, sessionEventListeners); | |
} | |
/** | |
* Create a new XMLContext based on the specified Project and ClassLoader. | |
* | |
* @param project An EclipseLink project | |
* @param classLoader The ClassLoader to be used | |
* @param sessionEventListeners If non-null, these listeners will be registered with the SessionEventManager | |
* @see SessionEventListener | |
* @see SessionEventManager | |
*/ | |
public XMLContext(Project project, ClassLoader classLoader, Collection<SessionEventListener> sessionEventListeners) { | |
contextState = new XMLContextState(this, project, classLoader, sessionEventListeners); | |
} | |
public XMLContext(Collection projects) { | |
this(projects, Thread.currentThread().getContextClassLoader()); | |
} | |
public XMLContext(Collection projects, ClassLoader classLoader) { | |
contextState = new XMLContextState(this, projects, classLoader); | |
} | |
/** | |
* INTERNAL: Return the XMLContextState that represents the XMLContexts | |
* stateful information. This method is provided for the benefits of layers | |
* that build on top of the core OXM layer such as MOXy's JAXB | |
* implementation. | |
*/ | |
public XMLContextState getXMLContextState() { | |
return (XMLContextState) contextState; | |
} | |
/** | |
* INTERNAL: Set the stateful information for this XMLContext. Once the new | |
* state has been set, OXM operations (marshal, unmarshal, etc) will be | |
* based on this new state. This method is provided for the benefit of | |
* layers that build on top of the core OXM layer such as MOXy's JAXB | |
* | |
*/ | |
public void setXMLContextState(XMLContextState xcs) { | |
synchronized(this) { | |
this.contextState = xcs; | |
} | |
} | |
/** | |
* INTERNAL: Add and initialize a new session to the list of sessions | |
* associated with this XMLContext. | |
*/ | |
public void addSession(DatabaseSession sessionToAdd) { | |
getXMLContextState().addSession(sessionToAdd); | |
} | |
/** | |
* Create a new XMLUnmarshaller | |
* | |
* @return An XMLUnmarshaller based on this XMLContext | |
*/ | |
public XMLUnmarshaller createUnmarshaller() { | |
return new XMLUnmarshaller(this); | |
} | |
/** | |
* Create a new XMLUnmarshaller | |
* <pre> | |
* Map<String, Boolean> parserFeatures = new HashMap<String, Boolean>(1); | |
* parserFeatures.put("http://apache.org/xml/features/validation/schema/normalized-value", false); | |
* XMLUnmarshaller unmarshaller = xmlContext.createUnmarshaller(parserFeatures); | |
* </pre> | |
* @return An XMLUnmarshaller based on this XMLContext, the underlying | |
* parser will use the passed in parser features. | |
*/ | |
public XMLUnmarshaller createUnmarshaller(Map<String, Boolean> parserFeatures) { | |
return new XMLUnmarshaller(this, parserFeatures); | |
} | |
/** | |
* Create a new XMLBinder | |
* @return an XMLBinder based on this XMLContext | |
*/ | |
public XMLBinder createBinder() { | |
return new XMLBinder(this); | |
} | |
/** | |
* Create a new XMLBinder with supplied marshaller and unmarshaller instances. | |
* @return an XMLBinder based on this XMLContext, | |
*/ | |
public XMLBinder createBinder(XMLMarshaller marshaller, XMLUnmarshaller unmarshaller) { | |
return new XMLBinder(this, marshaller, unmarshaller); | |
} | |
/** | |
* Create a new XMLMarshaller | |
* | |
* @return An XMLMarshaller based on this XMLContext | |
*/ | |
public XMLMarshaller createMarshaller() { | |
return new XMLMarshaller(this); | |
} | |
/** | |
* Create a new XMLValidator | |
* | |
* @return An XMLValidator based on this XMLContext | |
*/ | |
public XMLValidator createValidator() { | |
return new XMLValidator(this); | |
} | |
/** | |
* 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) { | |
return getSession(object); | |
} | |
/** | |
* 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) { | |
return super.getSession(clazz); | |
} | |
/** | |
* 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) { | |
return super.getSession(xmlDescriptor); | |
} | |
/** | |
* INTERNAL: Return the EclipseLink session used to marshal. | |
*/ | |
public List getSessions() { | |
return getXMLContextState().getSessions(); | |
} | |
/** | |
* 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) { | |
return getXMLContextState().getSession(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) { | |
return super.getSession(object); | |
} | |
/** | |
* 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) { | |
return super.getSession(clazz); | |
} | |
/** | |
* 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) { | |
return super.getSession(xmlDescriptor); | |
} | |
/** | |
* INTERNAL: | |
*/ | |
public void storeXMLDescriptorByQName(XMLDescriptor xmlDescriptor) { | |
contextState.storeDescriptorByQName(xmlDescriptor, null, null); | |
} | |
/** | |
* INTERNAL: Return the XMLDescriptor with the default root mapping matching | |
* the QName parameter. | |
*/ | |
public XMLDescriptor getDescriptor(QName qName) { | |
return super.getDescriptor(qName); | |
} | |
/** | |
* INTERNAL: Return the XMLDescriptor with the default root mapping matching | |
* the QName parameter. | |
*/ | |
public XMLDescriptor getDescriptor(XPathQName xpathQName) { | |
return super.getDescriptor(xpathQName); | |
} | |
public void addDescriptorByQName(QName qName, XMLDescriptor descriptor) { | |
contextState.addDescriptorByQName(qName, descriptor); | |
} | |
/** | |
* INTERNAL: Return the XMLDescriptor mapped to the global type matching the | |
* XPathFragment parameter. | |
*/ | |
public XMLDescriptor getDescriptorByGlobalType(XPathFragment xPathFragment) { | |
return super.getDescriptorByGlobalType(xPathFragment); | |
} | |
/** | |
* INTERNAL: | |
* Return the DocumentPreservationPolicy associated with this session | |
* @param session | |
* @return | |
*/ | |
public DocumentPreservationPolicy getDocumentPreservationPolicy(AbstractSession session) { | |
if (session == null) { | |
return null; | |
} | |
XMLLogin login = (XMLLogin) session.getDatasourceLogin(); | |
return login.getDocumentPreservationPolicy(); | |
} | |
public void setupDocumentPreservationPolicy(DatabaseSession session) { | |
getXMLContextState().setupDocumentPreservationPolicy(session); | |
} | |
/** | |
* 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 getXMLContextState().hasDocumentPreservation(); | |
} | |
/** | |
* ADVANCED: | |
* Adjust the OXM metadata to take into account 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(); | |
Mapping oxmMapping = (Mapping) oxmDescriptor.getMappingForAttributeName(ormMapping.getAttributeName()); | |
if(oxmMapping != null) { | |
CoreAttributeAccessor 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 | |
CoreAttributeAccessor containerAccessor = null; | |
Class containerClass = null; | |
if(oxmMapping.isAbstractCompositeObjectMapping()) { | |
containerAccessor = ((CompositeObjectMapping)oxmMapping).getInverseReferenceMapping().getAttributeAccessor(); | |
containerClass = ((CompositeObjectMapping)oxmMapping).getReferenceClass(); | |
} else if(oxmMapping.isAbstractCompositeCollectionMapping()) { | |
containerAccessor = ((CompositeCollectionMapping)oxmMapping).getInverseReferenceMapping().getAttributeAccessor(); | |
containerClass = ((CompositeCollectionMapping)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.isAbstractCompositeObjectMapping()) { | |
((CompositeObjectMapping)oxmMapping).getInverseReferenceMapping().setAttributeAccessor(ormAccessor); | |
} else if(oxmMapping.isAbstractCompositeCollectionMapping()) { | |
((CompositeCollectionMapping)oxmMapping).getInverseReferenceMapping().setAttributeAccessor(ormAccessor); | |
} | |
} | |
} | |
} | |
} | |
} | |
Iterator<DatabaseMapping> oxmMappingsIterator = oxmDescriptor.getMappings().iterator(); | |
while(oxmMappingsIterator.hasNext()) { | |
//iterate over the oxm mappings. Any ReferenceMappings that have a | |
//collection as a backpointer, check to see if the container policy | |
//needs to be matched with the ORM project | |
DatabaseMapping nextMapping = oxmMappingsIterator.next(); | |
if(nextMapping instanceof ObjectReferenceMapping) { | |
ObjectReferenceMapping refMapping = (ObjectReferenceMapping)nextMapping; | |
if(refMapping.getInverseReferenceMapping().getAttributeAccessor() != null && refMapping.getInverseReferenceMapping().getContainerPolicy() != null) { | |
ClassDescriptor refDescriptor = ormSession.getClassDescriptor(refMapping.getReferenceClass()); | |
if(refDescriptor != null) { | |
DatabaseMapping backpointerMapping =refDescriptor.getMappingForAttributeName(refMapping.getInverseReferenceMapping().getAttributeName()); | |
if(backpointerMapping != null && backpointerMapping.isCollectionMapping()) { | |
refMapping.getInverseReferenceMapping().getContainerPolicy().setContainerClass(((CollectionMapping)backpointerMapping).getContainerPolicy().getContainerClass()); | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
/** | |
* <p>Query the object model based on the corresponding XML document. The following pairings are equivalent:</p> | |
* | |
* <i>Return the Customer's ID</i> | |
* <pre> Integer id = xmlContext.getValueByXPath(customer, "@id", null, Integer.class); | |
* Integer id = customer.getId();</pre> | |
* | |
* <i>Return the Customer's Name</i> | |
* <pre> String name = xmlContext.getValueByXPath(customer, "ns:personal-info/ns:name/text()", null, String.class); | |
* String name = customer.getName();</pre> | |
* | |
* <i>Return the Customer's Address</i> | |
* <pre> Address address = xmlContext.getValueByXPath(customer, "ns:contact-info/ns:address", aNamespaceResolver, Address.class); | |
* Address address = customer.getAddress();</pre> | |
* | |
* <i>Return all the Customer's PhoneNumbers</i> | |
* <pre> List phoneNumbers = xmlContext.getValueByXPath(customer, "ns:contact-info/ns:phone-number", aNamespaceResolver, List.class); | |
* List phoneNumbers = customer.getPhoneNumbers();</pre> | |
* | |
* <i>Return the Customer's second PhoneNumber</i> | |
* <pre> PhoneNumber phoneNumber = xmlContext.getValueByXPath(customer, "ns:contact-info/ns:phone-number[2]", aNamespaceResolver, PhoneNumber.class); | |
* PhoneNumber phoneNumber = customer.getPhoneNumbers().get(1);</pre> | |
* | |
* <i>Return the base object</i> | |
* <pre> Customer customer = xmlContext.getValueByXPath(customer, ".", aNamespaceResolver, Customer.class); | |
* Customer customer = customer; | |
* </pre> | |
* | |
* @param <T> The return type of this method corresponds to the returnType parameter. | |
* @param object The XPath will be executed relative to this object. | |
* @param xPath The XPath statement | |
* @param namespaceResolver A NamespaceResolver containing the prefix/URI pairings from the XPath statement. | |
* @param returnType The return type. | |
* @return The object corresponding to the XPath or null if no result was found. | |
*/ | |
public <T> T getValueByXPath(Object object, String xPath, NamespaceResolver namespaceResolver, Class<T> returnType) { | |
return super.getValueByXPath(object, xPath, namespaceResolver, returnType); | |
} | |
/** | |
* <p>Set values in the object model based on the corresponding XML document. The following pairings are equivalent:</p> | |
* | |
* <i>Set the Customer's ID</i> | |
* <pre> xmlContext.setValueByXPath(customer, "@id", null, new Integer(123)); | |
* customer.setId(new Integer(123));</pre> | |
* | |
* <i>Set the Customer's Name</i> | |
* <pre> xmlContext.setValueByXPath(customer, "ns:personal-info/ns:name/text()", aNamespaceResolver, "Jane Doe"); | |
* customer.setName("Jane Doe");</pre> | |
* | |
* <i>Set the Customer's Address</i> | |
* <pre> xmlContext.setValueByXPath(customer, "ns:contact-info/ns:address", aNamespaceResolver, anAddress); | |
* customer.setAddress(anAddress);</pre> | |
* | |
* <i>Set the Customer's PhoneNumbers</i> | |
* <pre> xmlContext.setValueByXPath(customer, "ns:contact-info/ns:phone-number", aNamespaceResolver, phoneNumbers); | |
* customer.setPhoneNumbers(phoneNumbers);</pre> | |
* | |
* <i>Set the Customer's second PhoneNumber</i> | |
* <pre> xmlContext.setValueByXPath(customer, "ns:contact-info/ns:phone-number[2]", aNamespaceResolver, aPhoneNumber); | |
* customer.getPhoneNumbers().get(1);</pre> | |
* | |
* @param object The XPath will be executed relative to this object. | |
* @param xPath The XPath statement | |
* @param namespaceResolver A NamespaceResolver containing the prefix/URI pairings from the XPath statement. | |
* @param value The value to be set. | |
*/ | |
public void setValueByXPath(Object object, String xPath, NamespaceResolver namespaceResolver, Object value) { | |
super.setValueByXPath(object, xPath, namespaceResolver, value); | |
} | |
/** | |
* Create a new object instance for a given XML namespace and name. | |
* | |
* @param namespace | |
* The namespace of the complex type to create a new Java instance of. | |
* @param typeName | |
* The XML type name to create a new Java instance of. | |
* @param isGlobalType | |
* True if the object to be created represents a global type, false if it | |
* represents a global element. | |
* | |
* @return | |
* An instance of the Java class mapped to the indicated XML type, or null | |
* if no result was found. | |
*/ | |
public Object createByQualifiedName(String namespace, String typeName, boolean isGlobalType) throws IllegalArgumentException { | |
QName qName = new QName(namespace, typeName); | |
XMLDescriptor d = null; | |
if (!isGlobalType) { | |
d = getDescriptor(qName); | |
} else { | |
XPathFragment frag = new XPathFragment(); | |
frag.setNamespaceURI(namespace); | |
frag.setLocalName(typeName); | |
d = getDescriptorByGlobalType(frag); | |
} | |
if (d == null) { | |
return null; | |
} | |
return d.getInstantiationPolicy().buildNewInstance(); | |
} | |
/** | |
* Create a new object instance for a given XPath, relative to the parentObject. | |
* | |
* @param <T> | |
* The return type of this method corresponds to the returnType parameter. | |
* @param parentObject | |
* The XPath will be executed relative to this object. | |
* @param xPath | |
* The XPath statement. | |
* @param namespaceResolver | |
* A NamespaceResolver containing the prefix/URI pairings from the XPath statement. | |
* @param returnType | |
* The return type. | |
* | |
* @return | |
* An instance of the Java class mapped to the supplied XML type, or null | |
* if no result was found. | |
*/ | |
public <T> T createByXPath(Object parentObject, String xPath, NamespaceResolver namespaceResolver, Class<T> returnType) { | |
return super.createByXPath(parentObject, xPath, namespaceResolver, returnType); | |
} | |
private static class XMLContextState extends ContextState<AbstractSession, XMLDescriptor, Project, DatabaseSession, SessionEventListener> { | |
private List<DatabaseSession> sessions; | |
private boolean hasDocumentPreservation = false; | |
protected XMLContextState(XMLContext xmlContext, Collection<Project> projects, ClassLoader classLoader) { | |
this.context = xmlContext; | |
sessions = new ArrayList(projects.size()); | |
Iterator<Project> iterator = projects.iterator(); | |
while (iterator.hasNext()) { | |
Project project = iterator.next(); | |
preLogin(project, classLoader); | |
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); | |
// don't turn off global static logging | |
// AbstractSessionLog.getLog().log(AbstractSessionLog.INFO, | |
// "ox_turn_global_logging_off", getClass()); | |
// AbstractSessionLog.getLog().setLevel(AbstractSessionLog.OFF); | |
setupSession(session); | |
sessions.add(session); | |
storeDescriptorsByQName(session); | |
} | |
} | |
private XMLContextState(XMLContext xmlContext, String sessionNames, ClassLoader classLoader, String xmlResource) { | |
super(); | |
this.context = xmlContext; | |
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--) { | |
storeDescriptorsByQName(sessions.get(x)); | |
} | |
} | |
protected XMLContextState(XMLContext xmlContext, Project project, | |
ClassLoader classLoader, | |
Collection<SessionEventListener> sessionEventListeners) { | |
super(xmlContext, project, classLoader, sessionEventListeners); | |
} | |
/** | |
* INTERNAL: Add and initialize a new session to the list of sessions | |
* associated with this XMLContext. | |
*/ | |
private 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<SessionEventListener> listeners = sessionToAdd.getEventManager().getListeners(); | |
int listenersSize = listeners.size(); | |
for (int x = 0; x < listenersSize; x++) { | |
session.getEventManager().addListener(listeners.get(x)); | |
} | |
} | |
session.setExceptionHandler(sessionToAdd.getExceptionHandler()); | |
session.setLogLevel(SessionLog.OFF); | |
setupSession(session); | |
if(null == sessions) { | |
sessions = new ArrayList<DatabaseSession>(); | |
} | |
if(null != this.session) { | |
sessions.add(this.session); | |
} | |
sessions.add(session); | |
storeDescriptorsByQName(session); | |
} | |
@Override | |
protected void setupSession(DatabaseSession session) { | |
session.login(); | |
setupDocumentPreservationPolicy(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<SessionEventListener> listeners = dbSession.getEventManager().getListeners(); | |
int listenersSize = listeners.size(); | |
for (int x = 0; x < listenersSize; x++) { | |
session.getEventManager().addListener(listeners.get(x)); | |
} | |
} | |
session.setExceptionHandler(dbSession.getExceptionHandler()); | |
session.setLogLevel(SessionLog.OFF); | |
setupDocumentPreservationPolicy(session); | |
session.login(); | |
return session; | |
} | |
@Override | |
protected DatabaseSession getSession() { | |
if(null == sessions) { | |
return super.getSession(); | |
} | |
return sessions.get(0); | |
} | |
/** | |
* 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. | |
*/ | |
@Override | |
protected AbstractSession getSession(Class clazz) { | |
if (null == clazz) { | |
return null; | |
} | |
if (null == sessions) { | |
return super.getSession(clazz); | |
} | |
for (int x = 0, numberOfSessions = sessions.size(); 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. | |
*/ | |
@Override | |
protected AbstractSession getSession(XMLDescriptor xmlDescriptor) { | |
if (null == xmlDescriptor) { | |
return null; | |
} | |
if(null == sessions) { | |
return super.getSession(xmlDescriptor); | |
} | |
for (int x = 0, numberOfSessions = sessions.size(); x < numberOfSessions; x++) { | |
AbstractSession next = ((AbstractSession) sessions.get(x)); | |
if (next.getProject().getOrderedDescriptors().contains(xmlDescriptor)) { | |
return next; | |
} | |
} | |
throw XMLMarshalException.descriptorNotFoundInProject(xmlDescriptor.getJavaClass().getName()); | |
} | |
private List<DatabaseSession> getSessions() { | |
if(null == sessions) { | |
return Collections.singletonList(session); | |
} | |
return sessions; | |
} | |
/** | |
* INTERNAL: <code> | |
* XMLContext xmlContext = new XMLContext("path0:path1");<br> | |
* DatabaseSession session = xmlContext.getSession(0); // returns session for path0<br> | |
* </code> | |
*/ | |
private DatabaseSession getSession(int index) { | |
if (null == sessions) { | |
return session; | |
} | |
return 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. | |
*/ | |
@Override | |
protected AbstractSession getSession(Object object) { | |
if (null == object) { | |
return null; | |
} | |
if (null == sessions) { | |
return super.getSession(object); | |
} | |
for (int x = 0, numberOfSessions = sessions.size(); x < numberOfSessions; x++) { | |
AbstractSession next = ((AbstractSession) sessions.get(x)); | |
if (next.getDescriptor(object) != null) { | |
return next; | |
} | |
} | |
throw XMLMarshalException.descriptorNotFoundInProject(object.getClass().getName()); | |
} | |
/** | |
* INTERNAL: | |
* Return true if any session held onto by this context has a document preservation | |
* policy that requires unmarshalling from a Node. | |
*/ | |
private boolean hasDocumentPreservation() { | |
return this.hasDocumentPreservation; | |
} | |
@Override | |
protected void preLogin(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)); | |
} | |
} | |
private void setupDocumentPreservationPolicy(DatabaseSession session) { | |
XMLLogin login = (XMLLogin) session.getDatasourceLogin(); | |
if (login.getDocumentPreservationPolicy() == null) { | |
Iterator iterator = session.getProject().getOrderedDescriptors().iterator(); | |
while (iterator.hasNext()) { | |
Descriptor xmlDescriptor = (Descriptor) iterator.next(); | |
if (xmlDescriptor.shouldPreserveDocument()) { | |
login.setDocumentPreservationPolicy(new DescriptorLevelDocumentPreservationPolicy()); | |
break; | |
} | |
} | |
} | |
if (login.getDocumentPreservationPolicy() == null) { | |
login.setDocumentPreservationPolicy(new NoDocumentPreservationPolicy()); | |
} | |
login.getDocumentPreservationPolicy().initialize(context); | |
if (login.getDocumentPreservationPolicy().shouldPreserveDocument() && !hasDocumentPreservation) { | |
hasDocumentPreservation = true; | |
} | |
} | |
} | |
@Override | |
protected XMLField createField(String path) { | |
if(null == path) { | |
return new XMLField(); | |
} | |
return new XMLField(path); | |
} | |
} |