/*******************************************************************************
* 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.internal.oxm;
import java.lang.reflect.Modifier;
import java.util.List;
import javax.xml.namespace.QName;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.exceptions.XMLMarshalException;
import org.eclipse.persistence.internal.oxm.record.MarshalContext;
import org.eclipse.persistence.internal.oxm.record.ObjectMarshalContext;
import org.eclipse.persistence.internal.oxm.record.deferred.CompositeObjectMappingContentHandler;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.converters.Converter;
import org.eclipse.persistence.oxm.NamespaceResolver;
import org.eclipse.persistence.oxm.XMLDescriptor;
import org.eclipse.persistence.oxm.XMLField;
import org.eclipse.persistence.oxm.XMLMarshaller;
import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping;
import org.eclipse.persistence.oxm.mappings.converters.XMLConverter;
import org.eclipse.persistence.oxm.record.MarshalRecord;
import org.eclipse.persistence.oxm.record.UnmarshalRecord;
import org.eclipse.persistence.oxm.schema.XMLSchemaReference;
import org.eclipse.persistence.sessions.Session;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
/**
* INTERNAL:
* <p><b>Purpose</b>: This is how the XML Composite Object Mapping is handled
* when used with the TreeObjectBuilder.</p>
*/
public class XMLCompositeObjectMappingNodeValue extends XMLRelationshipMappingNodeValue implements NullCapableValue {
private XMLCompositeObjectMapping xmlCompositeObjectMapping;
public XMLCompositeObjectMappingNodeValue(XMLCompositeObjectMapping xmlCompositeObjectMapping) {
super();
this.xmlCompositeObjectMapping = xmlCompositeObjectMapping;
}
/**
* Marshal any 'self' mapped attributes.
*
* @param xPathFragment
* @param marshalRecord
* @param object
* @param session
* @param namespaceResolver
* @param marshaller
* @return
*/
public boolean marshalSelfAttributes(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, AbstractSession session, NamespaceResolver namespaceResolver, XMLMarshaller marshaller) {
Object objectValue = xmlCompositeObjectMapping.getAttributeValueFromObject(object);
if (xmlCompositeObjectMapping.getConverter() != null) {
Converter converter = xmlCompositeObjectMapping.getConverter();
if (converter instanceof XMLConverter) {
objectValue = ((XMLConverter)converter).convertObjectValueToDataValue(objectValue, session, marshaller);
} else {
objectValue = converter.convertObjectValueToDataValue(objectValue, session);
}
}
XMLDescriptor descriptor = (XMLDescriptor)session.getDescriptor(objectValue);
TreeObjectBuilder objectBuilder = (TreeObjectBuilder)descriptor.getObjectBuilder();
return objectBuilder.marshalAttributes(marshalRecord, objectValue, session);
}
public boolean marshal(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, AbstractSession session, NamespaceResolver namespaceResolver) {
return marshal(xPathFragment, marshalRecord, object, session, namespaceResolver, ObjectMarshalContext.getInstance());
}
public boolean marshal(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, AbstractSession session, NamespaceResolver namespaceResolver, MarshalContext marshalContext) {
if (xmlCompositeObjectMapping.isReadOnly()) {
return false;
}
if (xPathFragment.hasLeafElementType()) {
marshalRecord.setLeafElementType(xPathFragment.getLeafElementType());
}
XMLMarshaller marshaller = marshalRecord.getMarshaller();
Object objectValue = marshalContext.getAttributeValue(object, xmlCompositeObjectMapping);
if (xmlCompositeObjectMapping.getConverter() != null) {
Converter converter = xmlCompositeObjectMapping.getConverter();
if (converter instanceof XMLConverter) {
objectValue = ((XMLConverter)converter).convertObjectValueToDataValue(objectValue, session, marshaller);
} else {
objectValue = converter.convertObjectValueToDataValue(objectValue, session);
}
}
if (null == objectValue) {
return xmlCompositeObjectMapping.getNullPolicy().compositeObjectMarshal(xPathFragment, marshalRecord, object, session, namespaceResolver);
}
if ((marshaller != null) && (marshaller.getMarshalListener() != null)) {
marshaller.getMarshalListener().beforeMarshal(objectValue);
}
XPathFragment groupingFragment = marshalRecord.openStartGroupingElements(namespaceResolver);
marshalRecord.closeStartGroupingElements(groupingFragment);
XMLDescriptor descriptor = (XMLDescriptor)session.getDescriptor(objectValue);
TreeObjectBuilder objectBuilder = (TreeObjectBuilder)descriptor.getObjectBuilder();
if (!xPathFragment.isSelfFragment()) {
getXPathNode().startElement(marshalRecord, xPathFragment, object, session, namespaceResolver, objectBuilder, objectValue);
}
List extraNamespaces = objectBuilder.addExtraNamespacesToNamespaceResolver(descriptor, marshalRecord, session);
writeExtraNamespaces(extraNamespaces, marshalRecord, session);
if ((xmlCompositeObjectMapping.getReferenceDescriptor() == null) && (descriptor.getSchemaReference() != null)) {
addTypeAttributeIfNeeded(descriptor, xmlCompositeObjectMapping, marshalRecord);
}
objectBuilder.buildRow(marshalRecord, objectValue, session, marshaller);
if (!xPathFragment.isSelfFragment()) {
marshalRecord.endElement(xPathFragment, namespaceResolver);
}
objectBuilder.removeExtraNamespacesFromNamespaceResolver(marshalRecord, extraNamespaces, session);
if ((marshaller != null) && (marshaller.getMarshalListener() != null)) {
marshaller.getMarshalListener().afterMarshal(objectValue);
}
return true;
}
public boolean startElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord, Attributes atts) {
try {
unmarshalRecord.removeNullCapableValue(this);
XMLDescriptor xmlDescriptor = (XMLDescriptor)xmlCompositeObjectMapping.getReferenceDescriptor();
if (null == xmlDescriptor) {
xmlDescriptor = findReferenceDescriptor(unmarshalRecord, atts, xmlCompositeObjectMapping);
}
/**
* Null Composite Objects are marshalled in 2 ways when the input XML node is empty.
* (1) as null
* - isNullRepresentedByEmptyNode = true
* (2) as empty object
* - isNullRepresentedByEmptyNode = false
* A deferred contentHandler is used to queue events until we are able to determine
* whether we are in one of empty/simple/complex state.
* Control is returned to the UnmarshalHandler after creation of (1) or (2) above is started.
* Object creation was deferred to the DeferredContentHandler
*/
// Check if we need to create the DeferredContentHandler based on policy state
if(xmlCompositeObjectMapping.getNullPolicy().isNullRepresentedByEmptyNode() || xmlCompositeObjectMapping.getNullPolicy().isNullRepresentedByXsiNil()) {
if(null != xmlDescriptor) {
// Process null capable value
String qnameString = xPathFragment.getLocalName();
if(xPathFragment.getPrefix() != null) {
qnameString = xPathFragment.getPrefix() +":" + qnameString;
}
CompositeObjectMappingContentHandler aHandler = new CompositeObjectMappingContentHandler(//
unmarshalRecord, this, xmlCompositeObjectMapping, atts, xPathFragment, xmlDescriptor);
// Send control to the handler
aHandler.startElement(xPathFragment.getNamespaceURI(), xPathFragment.getLocalName(), qnameString, atts);
unmarshalRecord.getXMLReader().setContentHandler(aHandler);
}
} else {
boolean isNull = xmlCompositeObjectMapping.getNullPolicy().valueIsNull(atts);
if (isNull) {
xmlCompositeObjectMapping.setAttributeValueInObject(unmarshalRecord.getCurrentObject(), null);
} else {
XMLField xmlFld = (XMLField)this.xmlCompositeObjectMapping.getField();
if (xmlFld.hasLastXPathFragment()) {
unmarshalRecord.setLeafElementType(xmlFld.getLastXPathFragment().getLeafElementType());
}
processChild(xPathFragment, unmarshalRecord, atts, xmlDescriptor);
}
}
} catch (SAXException e) {
throw XMLMarshalException.unmarshalException(e);
}
return true;
}
public void endElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord) {
if (null == unmarshalRecord.getChildRecord()) {
return;
}
Object object = unmarshalRecord.getChildRecord().getCurrentObject();
if (xmlCompositeObjectMapping.getConverter() != null) {
Converter converter = xmlCompositeObjectMapping.getConverter();
if (converter instanceof XMLConverter) {
object = ((XMLConverter)converter).convertDataValueToObjectValue(object, unmarshalRecord.getSession(), unmarshalRecord.getUnmarshaller());
} else {
object = converter.convertDataValueToObjectValue(object, unmarshalRecord.getSession());
}
}
// Set the child object on the parent
unmarshalRecord.setAttributeValue(object, xmlCompositeObjectMapping);
if(xmlCompositeObjectMapping.getContainerAccessor() != null) {
xmlCompositeObjectMapping.getContainerAccessor().setAttributeValueInObject(object, unmarshalRecord.getCurrentObject());
}
unmarshalRecord.setChildRecord(null);
}
public UnmarshalRecord buildSelfRecord(UnmarshalRecord unmarshalRecord, Attributes atts) {
try {
XMLDescriptor xmlDescriptor = (XMLDescriptor)xmlCompositeObjectMapping.getReferenceDescriptor();
if (xmlDescriptor.hasInheritance()) {
unmarshalRecord.setAttributes(atts);
Class clazz = xmlDescriptor.getInheritancePolicy().classFromRow(unmarshalRecord, unmarshalRecord.getSession());
if (clazz == null) {
// no xsi:type attribute - look for type indicator on the default root element
QName leafElementType = unmarshalRecord.getLeafElementType();
// if we have a user-set type, try to get the class from the inheritance policy
if (leafElementType != null) {
Object indicator = xmlDescriptor.getInheritancePolicy().getClassIndicatorMapping().get(leafElementType);
// if the inheritance policy does not contain the user-set type, throw an exception
if (indicator == null) {
throw DescriptorException.missingClassForIndicatorFieldValue(leafElementType, xmlDescriptor.getInheritancePolicy().getDescriptor());
}
clazz = (Class)indicator;
}
}
if (clazz != null) {
xmlDescriptor = (XMLDescriptor)unmarshalRecord.getSession().getDescriptor(clazz);
} else {
// since there is no xsi:type attribute, use the reference descriptor set
// on the mapping - make sure it is non-abstract
if (Modifier.isAbstract(xmlDescriptor.getJavaClass().getModifiers())) {
// need to throw an exception here
throw DescriptorException.missingClassIndicatorField(unmarshalRecord, xmlDescriptor.getInheritancePolicy().getDescriptor());
}
}
}
TreeObjectBuilder stob2 = (TreeObjectBuilder)xmlDescriptor.getObjectBuilder();
UnmarshalRecord childRecord = (UnmarshalRecord)stob2.createRecord(unmarshalRecord.getSession());
childRecord.setSelfRecord(true);
unmarshalRecord.setChildRecord(childRecord);
childRecord.setXMLReader(unmarshalRecord.getXMLReader());
childRecord.startDocument(this.xmlCompositeObjectMapping);
xmlCompositeObjectMapping.setAttributeValueInObject(unmarshalRecord.getCurrentObject(), childRecord.getCurrentObject());
if(xmlCompositeObjectMapping.getContainerAccessor() != null) {
xmlCompositeObjectMapping.getContainerAccessor().setAttributeValueInObject(childRecord.getCurrentObject(), unmarshalRecord.getCurrentObject());
}
return childRecord;
} catch (SAXException e) {
throw XMLMarshalException.unmarshalException(e);
}
}
public void setNullValue(Object object, Session session) {
xmlCompositeObjectMapping.setAttributeValueInObject(object, null);
}
public boolean isNullCapableValue() {
XMLField xmlField = (XMLField)xmlCompositeObjectMapping.getField();
if (xmlField.getLastXPathFragment().isSelfFragment) {
return false;
}
return xmlCompositeObjectMapping.getNullPolicy().getIsSetPerformedForAbsentNode();
}
protected void addTypeAttributeIfNeeded(XMLDescriptor descriptor, DatabaseMapping mapping, MarshalRecord marshalRecord) {
XMLSchemaReference xmlRef = descriptor.getSchemaReference();
if (xmlCompositeObjectMapping.shouldAddXsiType(marshalRecord, descriptor) && (xmlRef != null)) {
addTypeAttribute(descriptor, marshalRecord, xmlRef.getSchemaContext());
}
}
public XMLCompositeObjectMapping getMapping() {
return xmlCompositeObjectMapping;
}
}