org.eclipse.persistence.internal.oxm.XMLCompositeDirectCollectionMappingNodeValue Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 1998, 2020 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.StringTokenizer;
import javax.xml.namespace.QName;
import org.eclipse.persistence.internal.core.helper.CoreClassConstants;
import org.eclipse.persistence.internal.core.queries.CoreContainerPolicy;
import org.eclipse.persistence.internal.core.sessions.CoreAbstractSession;
import org.eclipse.persistence.internal.oxm.mappings.DirectCollectionMapping;
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;
import org.eclipse.persistence.oxm.mappings.nullpolicy.AbstractNullPolicy;
import org.eclipse.persistence.oxm.mappings.nullpolicy.XMLNullRepresentationType;
import org.xml.sax.Attributes;
/**
* INTERNAL:
* Purpose: This is how the XML Composite Direct Collection Mapping is
* handled when used with the TreeObjectBuilder.
*/
public class XMLCompositeDirectCollectionMappingNodeValue extends MappingNodeValue implements ContainerValue {
private static final String SPACE = " ";
private DirectCollectionMapping xmlCompositeDirectCollectionMapping;
private int index = -1;
public XMLCompositeDirectCollectionMappingNodeValue(DirectCollectionMapping xmlCompositeDirectCollectionMapping) {
super();
this.xmlCompositeDirectCollectionMapping = xmlCompositeDirectCollectionMapping;
}
@Override
public boolean isOwningNode(XPathFragment xPathFragment) {
XPathFragment nextFragment = xPathFragment.getNextFragment();
if (nextFragment == null || xmlCompositeDirectCollectionMapping.usesSingleNode()) {
return xPathFragment.isAttribute() || xPathFragment.nameIsText();
} else {
return (nextFragment != null) && (nextFragment.nameIsText() || nextFragment.isAttribute());
}
}
/**
* Override the method in XPathNode such that the marshaller can be set on the
* marshalRecord - this is required for XMLConverter usage.
*/
@Override
public boolean marshal(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, CoreAbstractSession session, NamespaceResolver namespaceResolver) {
if (xmlCompositeDirectCollectionMapping.isReadOnly()) {
return false;
}
CoreContainerPolicy cp = getContainerPolicy();
Object collection = xmlCompositeDirectCollectionMapping.getAttributeAccessor().getAttributeValueFromObject(object);
if (null == collection) {
AbstractNullPolicy wrapperNP = xmlCompositeDirectCollectionMapping.getWrapperNullPolicy();
if (wrapperNP != null && wrapperNP.getMarshalNullRepresentation() == XMLNullRepresentationType.XSI_NIL) {
marshalRecord.nilSimple(namespaceResolver);
return true;
} else {
return false;
}
}
Object iterator = cp.iteratorFor(collection);
Field xmlField = (Field) xmlCompositeDirectCollectionMapping.getField();
if (null != iterator && cp.hasNext(iterator)) {
XPathFragment groupingFragment = marshalRecord.openStartGroupingElements(namespaceResolver);
marshalRecord.closeStartGroupingElements(groupingFragment);
} else {
if (xmlField.usesSingleNode() && !xmlCompositeDirectCollectionMapping.isDefaultEmptyContainer()) {
XPathFragment groupingFragment = marshalRecord.openStartGroupingElements(namespaceResolver);
marshalRecord.closeStartGroupingElements(groupingFragment);
} else {
return marshalRecord.emptyCollection(xPathFragment, namespaceResolver, xmlCompositeDirectCollectionMapping.getWrapperNullPolicy() != null);
}
}
Object objectValue;
if (xmlField.usesSingleNode()) {
StringBuilder stringValueStringBuilder = new StringBuilder();
String newValue;
QName schemaType = null;
if (xPathFragment != null && !xPathFragment.isAttribute() && !xPathFragment.nameIsText) {
marshalRecord.openStartElement(xPathFragment, namespaceResolver);
}
while (cp.hasNext(iterator)) {
objectValue = cp.next(iterator, session);
objectValue = xmlCompositeDirectCollectionMapping.convertObjectValueToDataValue(objectValue, session, marshalRecord.getMarshaller());
schemaType = xmlField.getSchemaTypeForValue(objectValue, session);
newValue = marshalRecord.getValueToWrite(schemaType, objectValue, (ConversionManager) session.getDatasourcePlatform().getConversionManager());
if (null != newValue) {
stringValueStringBuilder.append(newValue);
if (cp.hasNext(iterator)) {
stringValueStringBuilder.append(SPACE);
}
}
}
XPathFragment groupingFragment = marshalRecord.openStartGroupingElements(namespaceResolver);
if (xPathFragment != null && xPathFragment.isAttribute()) {
marshalRecord.attribute(xPathFragment, namespaceResolver, stringValueStringBuilder.toString());
marshalRecord.closeStartGroupingElements(groupingFragment);
} else {
marshalRecord.closeStartGroupingElements(groupingFragment);
if (xmlCompositeDirectCollectionMapping.isCDATA()) {
marshalRecord.cdata(stringValueStringBuilder.toString());
} else {
marshalRecord.characters(stringValueStringBuilder.toString());
if (xPathFragment != null && !xPathFragment.isAttribute() && !xPathFragment.nameIsText) {
marshalRecord.endElement(xPathFragment, namespaceResolver);
}
}
}
} else {
int valueSize = cp.sizeFor(collection);
if(marshalRecord.getMarshaller().isApplicationJSON() && (valueSize > 1 || !marshalRecord.getMarshaller().isReduceAnyArrays())) {
marshalRecord.startCollection();
}
while (cp.hasNext(iterator)) {
objectValue = cp.next(iterator, session);
marshalSingleValue(xPathFragment, marshalRecord, object, objectValue, session, namespaceResolver, ObjectMarshalContext.getInstance());
}
if(marshalRecord.getMarshaller().isApplicationJSON() && (valueSize > 1 || !marshalRecord.getMarshaller().isReduceAnyArrays())) {
marshalRecord.endCollection();
}
}
return true;
}
@Override
public void attribute(UnmarshalRecord unmarshalRecord, String namespaceURI, String localName, String value) {
Object collection = unmarshalRecord.getContainerInstance(this);
if (xmlCompositeDirectCollectionMapping.usesSingleNode()) {
StringTokenizer stringTokenizer = new StringTokenizer(value);
while (stringTokenizer.hasMoreTokens()) {
addUnmarshalValue(unmarshalRecord, stringTokenizer.nextToken(), collection);
}
} else {
addUnmarshalValue(unmarshalRecord, value, collection);
}
}
@Override
public boolean startElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord, Attributes atts) {
Field xmlField = (Field) xmlCompositeDirectCollectionMapping.getField();
XPathFragment lastXPathFragment = xmlField.getLastXPathFragment();
if (lastXPathFragment.nameIsText()) {
String type = atts.getValue(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, Constants.SCHEMA_TYPE_ATTRIBUTE);
if (null != type) {
String namespaceURI = null;
int colonIndex = type.indexOf(Constants.COLON);
if (colonIndex > -1) {
String prefix = type.substring(0, colonIndex);
namespaceURI = unmarshalRecord.resolveNamespacePrefix(prefix);
type = type.substring(colonIndex + 1);
}
unmarshalRecord.setTypeQName(new QName(namespaceURI, type));
}
} else if (lastXPathFragment.isAttribute()) {
if (!xmlField.usesSingleNode()) {
String namespaceURI = lastXPathFragment.getNamespaceURI();
if (namespaceURI == null) {
namespaceURI = Constants.EMPTY_STRING;
}
String value = atts.getValue(namespaceURI, lastXPathFragment.getLocalName());
Object collection = unmarshalRecord.getContainerInstance(this);
addUnmarshalValue(unmarshalRecord, value, collection);
}
}
return true;
}
@Override
public void endElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord) {
Field xmlField = (Field) xmlCompositeDirectCollectionMapping.getField();
Object value = unmarshalRecord.getCharacters().toString();
if(((String)value).length() == 0 && !xmlField.usesSingleNode()){
if( xmlCompositeDirectCollectionMapping.getNullValue() != null){
value = xmlCompositeDirectCollectionMapping.getNullValue();
}
}
unmarshalRecord.resetStringBuffer();
XPathFragment lastXPathFragment = xmlField.getLastXPathFragment();
if (!lastXPathFragment.nameIsText()) {
return;
}
if (xmlField.usesSingleNode() ) {
Object collection = unmarshalRecord.getContainerInstance(this);
StringTokenizer stringTokenizer = new StringTokenizer((String)value);
while (stringTokenizer.hasMoreTokens()) {
addUnmarshalValue(unmarshalRecord, stringTokenizer.nextToken(), collection);
}
} else {
if(!unmarshalRecord.getXMLReader().isInCollection() && unmarshalRecord.isNil() ){
unmarshalRecord.setAttributeValueNull(this);
}else{
Object collection = unmarshalRecord.getContainerInstance(this);
addUnmarshalValue(unmarshalRecord, value, collection);
}
}
}
@Override
public void endElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord, Object collection) {
Field xmlField = (Field) xmlCompositeDirectCollectionMapping.getField();
Object value = unmarshalRecord.getCharacters().toString();
if(((String)value).length() == 0 && !xmlField.usesSingleNode()){
if( xmlCompositeDirectCollectionMapping.getNullValue() != null){
value = xmlCompositeDirectCollectionMapping.getNullValue();
}
}
unmarshalRecord.resetStringBuffer();
if (xmlField.usesSingleNode() && value instanceof String) {
StringTokenizer stringTokenizer = new StringTokenizer((String)value);
while (stringTokenizer.hasMoreTokens()) {
addUnmarshalValue(unmarshalRecord, stringTokenizer.nextToken(), collection);
}
} else {
if (xmlField.getLastXPathFragment().nameIsText()) {
if(!unmarshalRecord.getXMLReader().isInCollection() && unmarshalRecord.isNil() ){
unmarshalRecord.setAttributeValueNull(this);
} else{
addUnmarshalValue(unmarshalRecord, value, collection);
}
}
}
}
private void addUnmarshalValue(UnmarshalRecord unmarshalRecord, Object value, Object collection) {
if (unmarshalRecord.isNil() && unmarshalRecord.getXMLReader().isNullRepresentedByXsiNil(xmlCompositeDirectCollectionMapping.getNullPolicy())){
value = null;
} else if (!isWhitespaceAware() && Constants.EMPTY_STRING.equals(value)) {
value = null;
}
Field xmlField = (Field) xmlCompositeDirectCollectionMapping.getField();
ConversionManager conversionManager = unmarshalRecord.getConversionManager();
if (unmarshalRecord.getTypeQName() != null) {
Class typeClass = xmlField.getJavaClass(unmarshalRecord.getTypeQName(), conversionManager);
value = conversionManager.convertObject(value, typeClass, unmarshalRecord.getTypeQName());
} else {
value = unmarshalRecord.getXMLReader().convertValueBasedOnSchemaType(xmlField, value, conversionManager, unmarshalRecord);
}
value = xmlCompositeDirectCollectionMapping.convertDataValueToObjectValue(value, unmarshalRecord.getSession(), unmarshalRecord.getUnmarshaller());
if (value != null && value.getClass() == CoreClassConstants.STRING) {
if (xmlCompositeDirectCollectionMapping.isCollapsingStringValues()) {
value = conversionManager.collapseStringValue((String)value);
} else if (xmlCompositeDirectCollectionMapping.isNormalizingStringValues()) {
value = conversionManager.normalizeStringValue((String)value);
}
}
unmarshalRecord.addAttributeValue(this, value, collection);
}
@Override
public Object getContainerInstance() {
return getContainerPolicy().containerInstance();
}
@Override
public void setContainerInstance(Object object, Object containerInstance) {
xmlCompositeDirectCollectionMapping.setAttributeValueInObject(object, containerInstance);
}
@Override
public CoreContainerPolicy getContainerPolicy() {
return xmlCompositeDirectCollectionMapping.getContainerPolicy();
}
@Override
public boolean isContainerValue() {
return true;
}
@Override
public boolean marshalSingleValue(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, Object value, CoreAbstractSession session, NamespaceResolver namespaceResolver, MarshalContext marshalContext) {
value = xmlCompositeDirectCollectionMapping.convertObjectValueToDataValue(value, session, marshalRecord.getMarshaller());
if (null != value) {
Field xmlField = (Field) xmlCompositeDirectCollectionMapping.getField();
QName schemaType = xmlField.getSchemaTypeForValue(value, session);
boolean isElementOpen = false;
if (Constants.QNAME_QNAME.equals(schemaType)) {
QName fieldValue = (QName) value;
if ((fieldValue.getNamespaceURI() == null || fieldValue.getNamespaceURI().equals("")) && marshalRecord.getNamespaceResolver().getDefaultNamespaceURI() != null) {
// In this case, an extra xmlns="" declaration is going to be added. This may
// require adjusting the namespace of the current fragment.
String defaultNamespaceURI = namespaceResolver.getDefaultNamespaceURI();
if (defaultNamespaceURI.equals(xPathFragment.getNamespaceURI()) && xPathFragment.getPrefix() == null) {
String prefix = namespaceResolver.generatePrefix();
String xPath = prefix + Constants.COLON + xPathFragment.getShortName();
XPathFragment newFragment = new XPathFragment(xPath);
newFragment.setNamespaceURI(defaultNamespaceURI);
newFragment.setNextFragment(xPathFragment.getNextFragment());
marshalRecord.openStartElement(newFragment, namespaceResolver);
isElementOpen = true;
marshalRecord.namespaceDeclaration(prefix, defaultNamespaceURI);
marshalRecord.predicateAttribute(xPathFragment, namespaceResolver);
xPathFragment = newFragment;
}
}
}
if (!isElementOpen) {
marshalRecord.openStartElement(xPathFragment, namespaceResolver);
}
XPathFragment nextFragment = xPathFragment.getNextFragment();
if (nextFragment != null && nextFragment.isAttribute()) {
marshalRecord.predicateAttribute(xPathFragment, namespaceResolver);
marshalRecord.attribute(nextFragment, namespaceResolver, value,schemaType);
marshalRecord.closeStartElement();
} else {
if (xmlField.isTypedTextField()) {
updateNamespaces(schemaType, marshalRecord, xmlField);
}
marshalRecord.closeStartElement();
marshalRecord.predicateAttribute(xPathFragment, namespaceResolver);
marshalRecord.characters(schemaType, value, null, xmlCompositeDirectCollectionMapping.isCDATA());
}
marshalRecord.endElement(xPathFragment, namespaceResolver);
return true;
} else {
AbstractNullPolicy nullPolicy = xmlCompositeDirectCollectionMapping.getNullPolicy();
if (nullPolicy.getMarshalNullRepresentation() != XMLNullRepresentationType.ABSENT_NODE) {
marshalRecord.openStartElement(xPathFragment, namespaceResolver);
XPathFragment nextFragment = xPathFragment.getNextFragment();
nullPolicy.directMarshal(nextFragment, marshalRecord, object, session, namespaceResolver);
marshalRecord.endElement(xPathFragment, namespaceResolver);
return true;
}
}
return false;
}
@Override
public DirectCollectionMapping getMapping() {
return xmlCompositeDirectCollectionMapping;
}
@Override
public boolean isWhitespaceAware() {
return !xmlCompositeDirectCollectionMapping.getNullPolicy().isNullRepresentedByEmptyNode();
}
@Override
public boolean getReuseContainer() {
return getMapping().getReuseContainer();
}
/**
* INTERNAL:
* Used to track the index of the corresponding containerInstance in the containerInstances Object[] on UnmarshalRecord
*/
@Override
public void setIndex(int index){
this.index = index;
}
/**
* INTERNAL:
* Set to track the index of the corresponding containerInstance in the containerInstances Object[] on UnmarshalRecord
* Set during TreeObjectBuilder initialization
*/
@Override
public int getIndex(){
return index;
}
/**
* INTERNAL
* Return true if an empty container should be set on the object if there
* is no presence of the collection in the XML document.
* @since EclipseLink 2.3.3
*/
@Override
public boolean isDefaultEmptyContainer() {
return getMapping().isDefaultEmptyContainer();
}
@Override
public boolean isWrapperAllowedAsCollectionName() {
return true;
}
}