blob: 79d645d5564f3bcd4571b023a63d05ac5f0b0ad5 [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 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 v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.internal.oxm;
import java.util.ArrayList;
import javax.xml.namespace.QName;
import org.eclipse.persistence.core.sessions.CoreSession;
import org.eclipse.persistence.internal.core.sessions.CoreAbstractSession;
import org.eclipse.persistence.internal.oxm.mappings.DirectMapping;
import org.eclipse.persistence.internal.oxm.mappings.Field;
import org.eclipse.persistence.internal.oxm.record.MarshalContext;
import org.eclipse.persistence.internal.oxm.record.MarshalRecord;
import org.eclipse.persistence.internal.oxm.record.ObjectMarshalContext;
import org.eclipse.persistence.internal.oxm.record.UnmarshalRecord;
/**
* INTERNAL:
* <p><b>Purpose</b>: This is how the XML Direct Mapping is handled when used
* with the TreeObjectBuilder.</p>
*/
public class XMLDirectMappingNodeValue extends MappingNodeValue implements NullCapableValue {
private DirectMapping xmlDirectMapping;
public XMLDirectMappingNodeValue(DirectMapping xmlDirectMapping) {
this.xmlDirectMapping = xmlDirectMapping;
}
@Override
public void setXPathNode(XPathNode xPathNode) {
super.setXPathNode(xPathNode);
xmlDirectMapping.getNullPolicy().xPathNode(xPathNode, this);
if(((Field) xmlDirectMapping.getField()).isTypedTextField()) {
XPathFragment typeAttributeXPathFragment = new XPathFragment();
typeAttributeXPathFragment.setAttribute(true);
typeAttributeXPathFragment.setLocalName(Constants.SCHEMA_TYPE_ATTRIBUTE);
typeAttributeXPathFragment.setNamespaceURI(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);
xPathNode.getParent().addChild(typeAttributeXPathFragment, new TypeNodeValue(), null);
}
}
@Override
public boolean isOwningNode(XPathFragment xPathFragment) {
return xPathFragment.hasAttribute || xPathFragment.nameIsText;
}
@Override
public boolean marshal(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, CoreAbstractSession session, NamespaceResolver namespaceResolver) {
return marshal(xPathFragment, marshalRecord, object, session, namespaceResolver, ObjectMarshalContext.getInstance());
}
@Override
public boolean marshal(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, CoreAbstractSession session, NamespaceResolver namespaceResolver, MarshalContext marshalContext) {
if (xmlDirectMapping.isReadOnly()) {
return false;
}
Object objectValue = marshalContext.getAttributeValue(object, xmlDirectMapping);
return this.marshalSingleValue(xPathFragment, marshalRecord, object, objectValue, session, namespaceResolver, marshalContext);
}
@Override
public boolean marshalSingleValue(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, Object objectValue, CoreAbstractSession session, NamespaceResolver namespaceResolver, MarshalContext marshalContext) {
Object fieldValue = xmlDirectMapping.getFieldValue(objectValue, session, marshalRecord);
// Check for a null value
if (null == fieldValue) {
// Perform marshal operations based on the null policy
return xmlDirectMapping.getNullPolicy().directMarshal(xPathFragment, marshalRecord, object, session, namespaceResolver);
} else {
QName schemaType = ((Field) xmlDirectMapping.getField()).getSchemaTypeForValue(fieldValue, session);
XPathFragment groupingFragment = null;
boolean isQName = false;
if(Constants.QNAME_QNAME.equals(schemaType)) {
//if marshalling a QName, handle grouping elements here in case namespace adjustments need
//to happen
groupingFragment = openGroupingElementsForQName((QName)fieldValue, marshalRecord);
isQName = true;
}
if(groupingFragment == null) {
groupingFragment = marshalRecord.openStartGroupingElements(namespaceResolver);
}
if (xPathFragment.hasAttribute) {
marshalRecord.attribute(xPathFragment, namespaceResolver, fieldValue, schemaType);
marshalRecord.closeStartGroupingElements(groupingFragment);
} else {
if(((Field) xmlDirectMapping.getField()).getXPathFragment().nameIsText ){
XPathNode parentNode = xPathNode.getParent();
if(parentNode.getAttributeChildren() != null){
marshalRecord.forceValueWrapper();
}
}
Field xmlField = (Field) xmlDirectMapping.getField();
if (xmlField.isTypedTextField()) {
updateNamespaces(schemaType, marshalRecord, xmlField);
}
marshalRecord.closeStartGroupingElements(groupingFragment);
marshalRecord.characters(schemaType, fieldValue, null, xmlDirectMapping.isCDATA());
}
if(isQName) {
//check to see if the last grouping fragment was swapped
XPathFragment fragment = getLastGroupingFragment();
if(fragment != groupingFragment) {
marshalRecord.endElement(groupingFragment, namespaceResolver);
return false;
}
}
return true;
}
}
private XPathFragment getLastGroupingFragment() {
XPathFragment fragment = ((Field)this.getMapping().getField()).getXPathFragment();
if(fragment.hasAttribute || fragment.nameIsText) {
return null;
}
while(fragment.getNextFragment() != null) {
if(fragment.getNextFragment().nameIsText || fragment.getNextFragment().hasAttribute) {
return fragment;
}
fragment = fragment.getNextFragment();
}
return fragment;
}
private XPathFragment openGroupingElementsForQName(QName fieldValue, MarshalRecord marshalRecord) {
XPathFragment xPathFragment = null;
ArrayList<XPathNode> groupingElements = marshalRecord.getGroupingElements();
NamespaceResolver namespaceResolver = marshalRecord.getNamespaceResolver();
if((fieldValue.getNamespaceURI() == null || fieldValue.getNamespaceURI().equals(Constants.EMPTY_STRING)) && marshalRecord.getNamespaceResolver().getDefaultNamespaceURI() != null) {
//In this case, the last grouping element may need to have a new prefix generated.
for (int x = 0, groupingElementsSize = groupingElements.size(); x < groupingElementsSize; x++) {
XPathNode xPathNode = groupingElements.get(x);
xPathFragment = xPathNode.getXPathFragment();
if(x == (groupingElements.size() - 1) && namespaceResolver.getDefaultNamespaceURI().equals(xPathFragment.getNamespaceURI()) && xPathFragment.getPrefix() == null) {
String prefix = namespaceResolver.generatePrefix();
String xPath = prefix + Constants.COLON + xPathFragment.getShortName();
XPathFragment newFragment = new XPathFragment(xPath);
newFragment.setNamespaceURI(namespaceResolver.getDefaultNamespaceURI());
marshalRecord.openStartElement(newFragment, namespaceResolver);
marshalRecord.namespaceDeclaration(prefix, namespaceResolver.getDefaultNamespaceURI());
marshalRecord.predicateAttribute(xPathFragment, namespaceResolver);
xPathFragment = newFragment;
} else {
marshalRecord.openStartElement(xPathFragment, namespaceResolver);
marshalRecord.predicateAttribute(xPathFragment, namespaceResolver);
marshalRecord.closeStartElement();
}
}
marshalRecord.setGroupingElement(null);
}
return xPathFragment;
}
@Override
public void attribute(UnmarshalRecord unmarshalRecord, String namespaceURI, String localName, String value) {
unmarshalRecord.removeNullCapableValue(this);
Field xmlField = (Field) xmlDirectMapping.getField();
CoreAbstractSession session = unmarshalRecord.getSession();
Object realValue = unmarshalRecord.getXMLReader().convertValueBasedOnSchemaType(xmlField, value, (ConversionManager) session.getDatasourcePlatform().getConversionManager(), unmarshalRecord);
// Perform operations on the object based on the null policy
Object convertedValue = xmlDirectMapping.getAttributeValue(realValue, session, unmarshalRecord);
xmlDirectMapping.setAttributeValueInObject(unmarshalRecord.getCurrentObject(), convertedValue);
}
@Override
public void endElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord) {
if(unmarshalRecord.isNil() && xmlDirectMapping.getNullPolicy().isNullRepresentedByXsiNil()){
Object convertedValue = xmlDirectMapping.getAttributeValue(org.eclipse.persistence.oxm.record.XMLRecord.NIL, unmarshalRecord.getSession(), unmarshalRecord);
unmarshalRecord.setAttributeValue(convertedValue, xmlDirectMapping);
unmarshalRecord.resetStringBuffer();
return;
}
unmarshalRecord.removeNullCapableValue(this);
Field xmlField = (Field) xmlDirectMapping.getField();
if (!xmlField.getLastXPathFragment().nameIsText) {
return;
}
Object value;
CharSequence unmarshalRecordCharacters = unmarshalRecord.getCharacters();
if (unmarshalRecordCharacters.length() == 0) {
value = xmlDirectMapping.getNullValue();
} else {
value = unmarshalRecordCharacters.toString();
}
unmarshalRecord.resetStringBuffer();
CoreAbstractSession session = unmarshalRecord.getSession();
ConversionManager conversionManager = (ConversionManager) session.getDatasourcePlatform().getConversionManager();
QName typeQName = unmarshalRecord.getTypeQName();
if (typeQName != null) {
Class<?> typeClass = xmlField.getJavaClass(typeQName, conversionManager);
value = conversionManager.convertObject(value, typeClass, typeQName);
} else {
value = unmarshalRecord.getXMLReader().convertValueBasedOnSchemaType(xmlField, value, conversionManager, unmarshalRecord);
}
Object convertedValue = xmlDirectMapping.getAttributeValue(value, session, unmarshalRecord);
unmarshalRecord.setAttributeValue(convertedValue, xmlDirectMapping);
}
@Override
public void setNullValue(Object object, CoreSession session) {
Object value = xmlDirectMapping.getObjectValue(null, session);
xmlDirectMapping.setAttributeValueInObject(object, value);
}
@Override
public boolean isNullCapableValue() {
if(xmlDirectMapping.getAttributeAccessor().isInstanceVariableAttributeAccessor() && !xmlDirectMapping.hasConverter() && xmlDirectMapping.getNullValue() == null) {
return false;
}
return xmlDirectMapping.getNullPolicy().getIsSetPerformedForAbsentNode();
}
@Override
public DirectMapping getMapping() {
return xmlDirectMapping;
}
@Override
public boolean isWhitespaceAware() {
return !xmlDirectMapping.getNullPolicy().isNullRepresentedByEmptyNode();
}
}