org.eclipse.persistence.oxm.mappings.XMLFragmentCollectionMapping 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, 2018 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.DatabaseField;
import org.eclipse.persistence.internal.identitymaps.CacheKey;
import org.eclipse.persistence.internal.oxm.mappings.FragmentCollectionMapping;
import org.eclipse.persistence.internal.oxm.mappings.XMLContainerMapping;
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.foundation.AbstractCompositeDirectCollectionMapping;
import org.eclipse.persistence.oxm.XMLField;
import org.eclipse.persistence.oxm.mappings.nullpolicy.AbstractNullPolicy;
import org.eclipse.persistence.oxm.record.DOMRecord;
import org.eclipse.persistence.oxm.record.XMLRecord;
import org.eclipse.persistence.platform.xml.XMLPlatformFactory;
import org.eclipse.persistence.queries.ObjectBuildingQuery;
import org.eclipse.persistence.sessions.Session;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
/**
* Purpose: This mapping provides a means to keep a part of the xml tree as a collection
* of DOM elements.
*
*
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 path and positional information; the last node in the XPath forms the local
* root node for the fragment. The XPath is specified on the mapping using the setXPath
* method.
*
*
*
* XPath
* Description
*
*
* phone-number
* The phone-number information is stored in the phone-number element.
*
*
* contact-info/phone-number
* The XPath statement may be used to specify any valid path.
*
*
* Sample Configuration:
*
* XMLFragmentCollectionMapping mapping = new XMLFragmentCollectionMapping();
* mapping.setAttributeName("phoneNumbers");
* mapping.setXPath("contact-info/phone-number");
*
*
*/
public class XMLFragmentCollectionMapping extends AbstractCompositeDirectCollectionMapping implements FragmentCollectionMapping, XMLMapping {
private boolean defaultEmptyContainer = XMLContainerMapping.EMPTY_CONTAINER_DEFAULT;
private boolean isWriteOnly;
private boolean reuseContainer;
private AbstractNullPolicy wrapperNullPolicy;
public XMLFragmentCollectionMapping() {
super();
}
/**
* INTERNAL:
*/
public boolean isXMLMapping() {
return true;
}
/**
* INTERNAL:
* Initialize the mapping.
*/
public void initialize(AbstractSession session) throws DescriptorException {
super.initialize(session);
}
/**
* Set the Mapping field name attribute to the given XPath String
* @param xpathString String
*/
public void setXPath(String xpathString) {
setField(new XMLField(xpathString));
}
/**
* Get the XPath String
* @return String the XPath String associated with this Mapping
*/
public String getXPath() {
return getFieldName();
}
/**
* INTERNAL:
* Build the nested collection from the database row.
*/
public Object valueFromRow(AbstractRecord row, JoinedAttributeManager joinManager, ObjectBuildingQuery sourceQuery, CacheKey cacheKey, AbstractSession executionSession, boolean isTargetProtected, Boolean[] wasCacheUsed) throws DatabaseException {
ContainerPolicy cp = this.getContainerPolicy();
Object fieldValue = ((DOMRecord)row).getValuesIndicatingNoEntry(this.getField(), true);
Vector nestedRows = null;
if (fieldValue instanceof Vector) {
nestedRows = (Vector)fieldValue;
}
if ((nestedRows == null) || nestedRows.isEmpty()) {
if (reuseContainer) {
Object currentObject = ((XMLRecord) row).getCurrentObject();
Object container = getAttributeAccessor().getAttributeValueFromObject(currentObject);
return container != null ? container : cp.containerInstance();
} else {
return cp.containerInstance();
}
}
Object result = null;
if (reuseContainer) {
Object currentObject = ((XMLRecord) row).getCurrentObject();
Object container = getAttributeAccessor().getAttributeValueFromObject(currentObject);
result = container != null ? container : cp.containerInstance();
} else {
result = cp.containerInstance();
}
for (Enumeration stream = nestedRows.elements(); stream.hasMoreElements();) {
Object next = stream.nextElement();
if (next instanceof Element) {
XMLPlatformFactory.getInstance().getXMLPlatform().namespaceQualifyFragment((Element)next);
}
cp.addInto(next, result, executionSession);
}
return result;
}
/**
* INTERNAL:
*/
@Override
public void writeFromObjectIntoRow(Object object, AbstractRecord row, AbstractSession session, WriteType writeType) {
if (this.isReadOnly()) {
return;
}
Object attributeValue = this.getAttributeValueFromObject(object);
if (attributeValue == null) {
row.put(this.getField(), null);
return;
}
ContainerPolicy cp = this.getContainerPolicy();
Vector elements = new Vector(cp.sizeFor(attributeValue));
for (Object iter = cp.iteratorFor(attributeValue); cp.hasNext(iter);) {
Object element = cp.next(iter, session);
if (element != null) {
elements.addElement(element);
}
}
Object fieldValue = null;
if (!elements.isEmpty()) {
fieldValue = this.getDescriptor().buildFieldValueFromDirectValues(elements, elementDataTypeName, session);
}
row.put(this.getField(), fieldValue);
}
public boolean isAbstractCompositeDirectCollectionMapping() {
return false;
}
public void writeSingleValue(Object attributeValue, Object parent, XMLRecord row, AbstractSession session) {
if (((XMLField)this.getField()).getLastXPathFragment().isAttribute()) {
if (attributeValue instanceof Attr) {
attributeValue = ((Attr)attributeValue).getValue();
}
} else if (((XMLField)this.getField()).getLastXPathFragment().nameIsText()) {
if (attributeValue instanceof Text) {
attributeValue = ((Text)attributeValue).getNodeValue();
}
}
row.put(getField(), attributeValue);
}
public boolean isWriteOnly() {
return this.isWriteOnly;
}
public void setIsWriteOnly(boolean b) {
this.isWriteOnly = b;
}
public void preInitialize(AbstractSession session) throws DescriptorException {
getAttributeAccessor().setIsWriteOnly(this.isWriteOnly());
getAttributeAccessor().setIsReadOnly(this.isReadOnly());
super.preInitialize(session);
}
public void setAttributeValueInObject(Object object, Object value) throws DescriptorException {
if(isWriteOnly()) {
return;
}
super.setAttributeValueInObject(object, value);
}
/**
* Return true if the original container on the object should be used if
* present. If it is not present then the container policy will be used to
* create the container.
*/
public boolean getReuseContainer() {
return reuseContainer;
}
/**
* Specify whether the original container on the object should be used if
* present. If it is not present then the container policy will be used to
* create the container.
*/
public void setReuseContainer(boolean reuseContainer) {
this.reuseContainer = reuseContainer;
}
/**
* 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
*/
public boolean isDefaultEmptyContainer() {
return defaultEmptyContainer;
}
/**
* INTERNAL
* Indicate whether by default an empty container should be set on the
* field/property if the collection is not present in the XML document.
* @since EclipseLink 2.3.3
*/
public void setDefaultEmptyContainer(boolean defaultEmptyContainer) {
this.defaultEmptyContainer = defaultEmptyContainer;
}
public AbstractNullPolicy getWrapperNullPolicy() {
return this.wrapperNullPolicy;
}
public void setWrapperNullPolicy(AbstractNullPolicy policy) {
this.wrapperNullPolicy = policy;
}
}