org.eclipse.persistence.oxm.mappings.XMLTransformationMapping Maven / Gradle / Ivy
Show all versions of eclipselink Show documentation
/*
* Copyright (c) 1998, 2024 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 org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.oxm.mappings.Field;
import org.eclipse.persistence.internal.oxm.mappings.TransformationMapping;
import org.eclipse.persistence.internal.oxm.record.UnmarshalRecord;
import org.eclipse.persistence.internal.oxm.record.XMLTransformationRecord;
import org.eclipse.persistence.internal.queries.ContainerPolicy;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.mappings.AttributeAccessor;
import org.eclipse.persistence.mappings.foundation.AbstractTransformationMapping;
import org.eclipse.persistence.mappings.transformers.FieldTransformer;
import org.eclipse.persistence.oxm.XMLField;
import org.eclipse.persistence.oxm.record.XMLRecord;
import org.eclipse.persistence.queries.ReadObjectQuery;
/**
* Transformation XML mappings are used to create a custom mapping where one or more XML nodes can
* be used to create the object to be stored in a Java class's attribute. To handle the custom
* requirements at marshal (write) and unmarshall (read) time, a transformation mapping takes instances
* of org.eclipse.persistence.mappings.transformers (such as AttributeTransformer and FieldTransformer), providing
* a non-intrusive solution that avoids the need for domain objects to implement any 'special' interfaces.
*
*
Setting the XPath: TopLink XML mappings make use of XPath statements 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
* field transformer that is set on the mapping. The XPath is set as the first parameter of the
* addFieldTransformer
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
*
*
* @name
* The "@" character indicates that the node is an attribute.
*
*
* text()
* "text()" indicates that the node is a text node. In this case the name value in the
* text node belongs to the context node.
*
*
* full-name/text()
* The name information is stored in the text node of the full-name element.
*
*
* personal-info/name/text()
* The XPath statement may be used to specify any valid path.
*
*
* name[2]/text()
* The XPath statement may contain positional information. In this case the name
* information is stored in the text node of the second occurrence of the name element.
*
*
*
* Mapping a transformation: A transformer can be configured to perform both the
* XML instance-to-Java attribute transformation at unmarshall time (via attribute transformer) and
* the Java attribute-to-XML instance transformation at marshal time (via field transformer).
*
*
*
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="name" type="xsd:string"/>
* <xsd:element name="normal-hours" type="normal-hours-type"/>
* </xsd:sequence>
* </xsd:complexType>
* <xsd:complexType name="normal-hours-type">
* <xsd:sequence>
* <xsd:element name="start-time" type="xsd:string"/>
* <xsd:element name="end-time" type="xsd:string"/>
* </xsd:sequence>
* </xsd:complexType>
* </xsd:schema>
*
*
*
Code Sample
*
* XMLTransformationMapping mapping = new XMLTransformationMapping();
* mapping.setAttributeName("normalHours");
* mapping.setAttributeTransformerClassName("org.eclipse.persistence.testing.oxm.mappings.transformation.NormalHoursAttributeTransformer");
* mapping.addFieldTransformer("normal-hours/start-time/text()", new StartTimeTransformer());
* mapping.addFieldTransformer("normal-hours/end-time/text()", new EndTimeTransformer()");
*
*
*
More Information: For more information about using the XML Transformation 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 XMLTransformationMapping extends AbstractTransformationMapping implements TransformationMapping, XMLMapping {
public XMLTransformationMapping() {
super();
}
/**
* INTERNAL:
*/
@Override
public boolean isXMLMapping() {
return true;
}
@Override
public void addFieldTransformer(String fieldName, FieldTransformer transformer) {
this.addFieldTransformer(new XMLField(fieldName), transformer);
}
@Override
public void addFieldTransformerClassName(String fieldName, String className) {
this.addFieldTransformerClassName(new XMLField(fieldName), className);
}
@Override
public void addFieldTransformation(String fieldName, String methodName) {
this.addFieldTransformation(new XMLField(fieldName), methodName);
}
@Override
public void writeSingleValue(Object value, Object parent, XMLRecord row, AbstractSession session) {
this.writeFromObjectIntoRow(parent, row, session, WriteType.UNDEFINED);
}
@Override
public void setIsWriteOnly(boolean b) {
// no op for this mapping.
// A Transformation Mapping is write only if it has no Attribute Transformations.
}
@Override
public void preInitialize(AbstractSession session) throws DescriptorException {
getAttributeAccessor().setIsWriteOnly(this.isWriteOnly());
getAttributeAccessor().setIsReadOnly(this.isReadOnly());
super.preInitialize(session);
}
/**
* INTERNAL:
* Put value into a record keyed on field.
* @since EclipseLink 2.6.0
*/
@Override
public void writeFromAttributeIntoRow(UnmarshalRecord unmarshalRecord, Field field, Object value, boolean isElement) {
if (null == unmarshalRecord.getTransformationRecord()) {
unmarshalRecord.setTransformationRecord(new XMLTransformationRecord("ROOT", unmarshalRecord));
}
Field toWrite = field;
if(isElement) {
boolean isCDATA = unmarshalRecord.isBufferCDATA();
if(field.isCDATA() != isCDATA) {
toWrite = new XMLField(field.getName());
toWrite.setNamespaceResolver(field.getNamespaceResolver());
toWrite.setIsCDATA(isCDATA);
}
}
unmarshalRecord.getTransformationRecord().put(toWrite, value);
}
/**
* INTERNAL:
* Extract value from the row and set the attribute to the value in the object.
* @since EclipseLink 2.6.0
*/
@Override
public Object readFromRowIntoObject(XMLRecord row, Object object, AbstractSession executionSession, boolean isTargetProtected) throws DatabaseException {
ReadObjectQuery query = new ReadObjectQuery();
query.setSession(executionSession);
return readFromRowIntoObject(row, null, object, null, query, executionSession, isTargetProtected);
}
}