org.eclipse.persistence.oxm.mappings.XMLCompositeDirectCollectionMapping Maven / Gradle / Ivy
Show all versions of eclipselink Show documentation
/*
* 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.oxm.mappings;
import java.util.Enumeration;
import java.util.Vector;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.identitymaps.CacheKey;
import org.eclipse.persistence.internal.oxm.XMLConversionManager;
import org.eclipse.persistence.internal.oxm.mappings.DirectCollectionMapping;
import org.eclipse.persistence.internal.oxm.mappings.XMLContainerMapping;
import org.eclipse.persistence.internal.queries.CollectionContainerPolicy;
import org.eclipse.persistence.internal.queries.ContainerPolicy;
import org.eclipse.persistence.internal.queries.JoinedAttributeManager;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.mappings.AttributeAccessor;
import org.eclipse.persistence.mappings.converters.Converter;
import org.eclipse.persistence.mappings.converters.TypeConversionConverter;
import org.eclipse.persistence.mappings.foundation.AbstractCompositeDirectCollectionMapping;
import org.eclipse.persistence.oxm.XMLConstants;
import org.eclipse.persistence.oxm.XMLField;
import org.eclipse.persistence.oxm.XMLMarshaller;
import org.eclipse.persistence.oxm.XMLUnmarshaller;
import org.eclipse.persistence.oxm.mappings.converters.XMLConverter;
import org.eclipse.persistence.oxm.mappings.nullpolicy.AbstractNullPolicy;
import org.eclipse.persistence.oxm.mappings.nullpolicy.NullPolicy;
import org.eclipse.persistence.oxm.mappings.nullpolicy.XMLNullRepresentationType;
import org.eclipse.persistence.oxm.record.DOMRecord;
import org.eclipse.persistence.oxm.record.XMLRecord;
import org.eclipse.persistence.queries.ObjectBuildingQuery;
import org.eclipse.persistence.sessions.Session;
/**
* Composite direct collection XML mappings map a collection of simple types (String, Number, Date,
* etc.) to and from a sequence of composite XML nodes.
*
*
Composite direct collection XML mappings can be used in the following scenarios:
* - Mapping to Multiple Text Nodes
* - Mapping to Multiple Attributes
* - Mapping to a Single Text Node
* - Mapping to a Single Attribute
* - Mapping to a List of Unions
* - Mapping a Union of Lists
* - Specifying the Content Type of a Collection
*
*
* Setting the XPath: TopLink XML mappings make use of an XPath statement to find the relevant
* data in an XML document. The XPath statement is relative to the context node specified in the descriptor.
* The XPath may contain node type, path, and positional information. The XPath is specified on the
* mapping using the setXPath
method.
*
*
The following XPath statements may be used to specify the location of XML data relating to an object's
* name attribute:
*
*
* XPath statements
*
* XPath
* Description
*
*
* @tasks
* The "@" character indicates that the node is an attribute. This XPath applies only to the single
* attribute node case; each member of the collection is mapped to a single node.
*
*
* tasks/@task
* The "@" character indicates that the node is an attribute. The information is stored
* in the attribute node of the tasks element.
*
*
* text()
* "text()" indicates that the node is a text node. In this case the task value in the
* text node belongs to the context node.
*
*
* tasks/text()
* The task information is stored in the text node of the tasks element.
*
*
* tasks/task/text()
* The XPath statement may be used to specify any valid path.
*
*
* task[2]/text()
* The XPath statement may contain positional information. In this case the task
* information is stored in the text node of the second occurrence of the task element.
*
*
*
* Mapping to a Single Text Node: By default, TopLink maps each member of a collection
* to it's own node. It is possible, however, to mapping a collection to a single node; here the contents of
* the node is treated as a space-separated list. This behavior is set on the mapping using the
* setUsesSingleNode
method, with 'true' as the parameter.
*
*
*
*
XML Schema
*
* <?xml version="1.0" encoding="UTF-8"?>
* <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
* <xsd:element name="employee" type="employee-type"/>
* <xsd:complexType name="employee-type">
* <xsd:sequence>
* <xsd:element name="tasks" type="tasks-type"/>
* </xsd:sequence>
* </xsd:complexType>
* <xsd:simpleType name="tasks-type">
* <xsd:list itemType="xsd:string"/>
* </xsd:simpleType>
* </xsd:schema>
*
*
*
Code Sample
*
* XMLCompositeDirectCollectionMapping tasksMapping = new XMLCompositeDirectCollectionMapping();
* tasksMapping.setAttributeName("tasks");
* tasksMapping.setXPath("tasks/text()");
* tasksMapping.setUsesSingleNode(true);
*
*
*
Specifying the Content Type of a Collection: By default, TopLink will treat the node values
* read in by a composite direct collection XML mapping as objects of type String. You can override this behavior
* by specifying the type of the collection's contents.
*
*
*
*
XML Schema
*
* <?xml version="1.0" encoding="UTF-8"?>
* <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
* <xsd:element name="employee" type="employee-type"/>
* <xsd:complexType name="employee-type">
* <xsd:sequence>
* <xsd:element name="vacation" type="xsd:string" maxOccurs="unbounded"/>
* </xsd:sequence>
* </xsd:complexType>
* </xsd:schema>
*
*
*
Code Sample
*
* XMLCompositeDirectCollectionMapping tasksMapping = new XMLCompositeDirectCollectionMapping();
* tasksMapping.setAttributeName("vacationDays");
* tasksMapping.setXPath("vacation/text()");
* tasksMapping.setAttributeElementClass(Calendar.class);
*
*
*
Mapping to a List of Unions:
*
*
*
*
XML Schema
*
* <?xml version="1.0" encoding="UTF-8"?>
* <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
* <xsd:element name="vacation" type="listOfUnions"/>
* <xsd:simpleType name="listOfUnions">
* <xsd:list>
* <xsd:simpleType>
* <xsd:union memberTypes="xsd:date xsd:integer"/>
* </xsd:simpleType>
* </xsd:list>
* </xsd:simpleType>
* </xsd:schema>
*
*
*
Code Sample
*
* XMLCompositeDirectCollectionMapping mapping = new XMLCompositeDirectCollectionMapping();
* mapping.setAttributeName("myattribute");
* XMLUnionField field = new XMLUnionField("listOfUnions/text()");
* mapping.addSchemaType(new QName(url,XMLConstants.INT));
* mapping.addSchemaType(new QName(url,XMLConstants.DATE));
* mapping.setField(field);
* mapping.useSingleElement(false);
*
*
*
More Information: For more information about using the XML Composite Direct
* Collection Mapping, see the "Understanding XML Mappings" chapter of the Oracle TopLink
* Developer's Guide.
*
* @since Oracle TopLink 10g Release 2 (10.1.3)
*/
public class XMLCompositeDirectCollectionMapping extends AbstractCompositeDirectCollectionMapping implements DirectCollectionMapping, XMLMapping, XMLNillableMapping {
private boolean isCDATA;
private boolean isDefaultEmptyContainer = XMLContainerMapping.EMPTY_CONTAINER_DEFAULT;
private boolean isWriteOnly;
private AbstractNullPolicy nullPolicy;
protected boolean reuseContainer;
private boolean isCollapsingStringValues;
private boolean isNormalizingStringValues;
private AbstractNullPolicy wrapperNullPolicy;
/** Support specification of the value to use for null. */
protected transient Object nullValue;
public XMLCompositeDirectCollectionMapping() {
super();
this.nullPolicy = new NullPolicy();
this.nullPolicy.setNullRepresentedByEmptyNode(true);
}
/**
* INTERNAL:
*/
@Override
public boolean isXMLMapping() {
return true;
}
/**
* INTERNAL:
* Initialize the mapping.
*/
@Override
public void initialize(AbstractSession session) throws DescriptorException {
super.initialize(session);
if (this.getField() instanceof XMLField) {
if (valueConverter instanceof TypeConversionConverter) {
TypeConversionConverter converter = (TypeConversionConverter) valueConverter;
this.getField().setType(converter.getObjectClass());
}
String xpathString = ((XMLField) getField()).getXPath();
if (this.isAbstractCompositeDirectCollectionMapping() && (xpathString.indexOf(XMLConstants.ATTRIBUTE) == -1) && (!xpathString.endsWith(XMLConstants.TEXT))) {
throw DescriptorException.invalidXpathForXMLDirectMapping(this);
}
}
ContainerPolicy cp = getContainerPolicy();
if (cp != null) {
if (cp.getContainerClass() == null) {
Class