blob: 48f3c92fd53c600c0405cc8adb89e0535f3de117 [file] [log] [blame]
/*
* 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
// 11/10/2011-2.4 Guy Pelletier
// - 357474: Address primaryKey option from tenant discriminator column
package org.eclipse.persistence.mappings.xdb;
import org.eclipse.persistence.exceptions.ConversionException;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.platform.database.XMLTypePlaceholder;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.mappings.DirectToFieldMapping;
import org.eclipse.persistence.platform.xml.XMLComparer;
import org.eclipse.persistence.platform.xml.XMLParser;
import org.eclipse.persistence.platform.xml.XMLPlatformFactory;
import org.eclipse.persistence.platform.xml.XMLTransformer;
import org.eclipse.persistence.sessions.Session;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
/**
* <b>Purpose</b>: Mapping used to map from a DOM (org.w3c.Document) or XML String into
* an Oracle XMLType field, in Oracle 9i XDB.
*
* @since Toplink 10.1.3
*/
public class DirectToXMLTypeMapping extends DirectToFieldMapping {
/**
* Indicates if we should initialize the whole DOM on a read.
* This is only used if the user is mapping from an Oracle Document implementation.
*/
protected boolean shouldReadWholeDocument = false;
/**
* Used to convert the DOM to a String.
*/
private XMLTransformer xmlTransformer;
/**
* Used to determine if the XML document has been modified.
*/
private XMLComparer xmlComparer;
/**
* Used to convert the String to a DOM
*/
private XMLParser xmlParser;
/**
* INTERNAL:
* Default to mutable if mapped as a DOM.
*/
@Override
public void preInitialize(AbstractSession session) throws DescriptorException {
if (this.attributeClassification == null) {
this.attributeClassification = getAttributeAccessor().getAttributeClass();
}
if ((this.isMutable == null) && (this.attributeClassification != ClassConstants.STRING)) {
setIsMutable(true);
}
super.preInitialize(session);
}
/**
* INTERNAL:
* The mapping is initialized with the given session. This mapping is fully initialized
* after this.
*/
@Override
public void initialize(AbstractSession session) throws DescriptorException {
super.initialize(session);
setFieldClassification(XMLTypePlaceholder.class);
}
public DirectToXMLTypeMapping() {
super();
this.xmlTransformer = XMLPlatformFactory.getInstance().getXMLPlatform().newXMLTransformer();
this.xmlTransformer.setFormattedOutput(false);
this.xmlParser = XMLPlatformFactory.getInstance().getXMLPlatform().newXMLParser();
this.xmlComparer = new XMLComparer();
}
/**
* PUBLIC:
* @param readWholeDocument - determines if the Oracle XDB DOM should be fully initialized
* on a read.
*/
public void setShouldReadWholeDocument(boolean readWholeDocument) {
this.shouldReadWholeDocument = readWholeDocument;
}
/**
* PUBLIC:
* @return boolean - returns true if currently initializing DOMs on reads.
*/
public boolean shouldReadWholeDocument() {
return shouldReadWholeDocument;
}
/**
* INTERNAL:
* Get the attribute value for the given field value. If we're mapping to a
* Document, we need to check if we should return the Oracle DOM or build a
* new one.
*/
@Override
public Object getObjectValue(Object fieldValue, Session session) throws ConversionException {
Object attributeValue = fieldValue;
try {
if (attributeValue != null) {
if (this.attributeClassification != ClassConstants.STRING) {
String xml = (String)attributeValue;
java.io.StringReader reader = new java.io.StringReader(xml);
return this.xmlParser.parse(reader);
}
}
} catch (Exception ex) {
throw ConversionException.couldNotBeConverted(fieldValue, this.attributeClassification, ex);
}
if ((attributeValue == null) && (this.nullValue != null)) {// Translate default null value
return this.nullValue;
}
// Allow for user defined conversion to the object value.
if (this.converter != null) {
attributeValue = this.converter.convertDataValueToObjectValue(attributeValue, session);
}
return attributeValue;
}
@Override
public boolean isDirectToXMLTypeMapping() {
return true;
}
/**
* INTERNAL:
* Clone the DOM Document if required.
*/
@Override
protected Object buildCloneValue(Object attributeValue, AbstractSession session) {
Object newAttributeValue = attributeValue;
if (isMutable() && attributeValue != null) {
if ((getAttributeClassification() == ClassConstants.DOCUMENT) || (getAttributeClassification() == ClassConstants.NODE)) {
Document doc = (Document)attributeValue;
newAttributeValue = doc.cloneNode(true);
}
}
return newAttributeValue;
}
/**
* INTERNAL:
* Compare the attribute values.
* Compare Nodes if mapped as a DOM.
*/
@Override
protected boolean compareObjectValues(Object firstValue, Object secondValue, AbstractSession session) {
// PERF: Check identity before conversion.
if (firstValue == secondValue) {
return true;
}
if ((firstValue == null) || (secondValue == null)) {
return false;
}
if (getAttributeClassification() == ClassConstants.STRING) {
return firstValue.equals(secondValue);
} else {
Object one = getFieldValue(firstValue, session);
Object two = getFieldValue(secondValue, session);
if (one instanceof Node && two instanceof Node) {
return this.xmlComparer.isNodeEqual((Node)one, (Node)two);
}
return one.equals(two);
}
}
}