/******************************************************************************* * Copyright (c) 1998, 2015 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 v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * 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); } } }