| /* |
| * Copyright (c) 1998, 2019 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.record; |
| |
| import java.io.Reader; |
| import java.io.StringReader; |
| import java.io.StringWriter; |
| import java.io.Writer; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| import java.util.Vector; |
| |
| import javax.xml.namespace.QName; |
| |
| import org.eclipse.persistence.internal.core.sessions.CoreAbstractSession; |
| import org.eclipse.persistence.internal.helper.DatabaseField; |
| import org.eclipse.persistence.internal.helper.Helper; |
| import org.eclipse.persistence.internal.oxm.ReferenceResolver; |
| import org.eclipse.persistence.internal.oxm.UnmarshalXPathEngine; |
| import org.eclipse.persistence.internal.oxm.XMLConversionManager; |
| import org.eclipse.persistence.oxm.IDResolver; |
| import org.eclipse.persistence.oxm.XMLField; |
| import org.eclipse.persistence.oxm.XMLConstants; |
| import org.eclipse.persistence.oxm.XMLLogin; |
| import org.eclipse.persistence.internal.oxm.XPathEngine; |
| import org.eclipse.persistence.internal.oxm.mappings.Field; |
| import org.eclipse.persistence.internal.oxm.record.TransformationRecord; |
| import org.eclipse.persistence.internal.sessions.AbstractRecord; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| import org.eclipse.persistence.oxm.NamespaceResolver; |
| import org.eclipse.persistence.oxm.mappings.nullpolicy.AbstractNullPolicy; |
| import org.eclipse.persistence.platform.xml.XMLParser; |
| import org.eclipse.persistence.platform.xml.XMLPlatform; |
| import org.eclipse.persistence.platform.xml.XMLPlatformFactory; |
| import org.eclipse.persistence.platform.xml.XMLTransformer; |
| import org.eclipse.persistence.exceptions.ValidationException; |
| import org.eclipse.persistence.exceptions.XMLMarshalException; |
| import org.w3c.dom.Attr; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.DocumentFragment; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| |
| /** |
| * PUBLIC: |
| * Provides a Record/Map API on an XML DOM element. |
| */ |
| public class DOMRecord extends XMLRecord implements TransformationRecord { |
| private Node dom; |
| private Node currentNode; |
| private XMLField lastUpdatedField; |
| private ReferenceResolver referenceResolver; |
| |
| /** |
| * INTERNAL: |
| * Default constructor. |
| */ |
| public DOMRecord() { |
| super(); |
| setNamespaceResolver(new NamespaceResolver()); |
| referenceResolver = new ReferenceResolver(); |
| // Required for subclasses. |
| } |
| |
| /** |
| * INTERNAL: |
| * Create a record with the root element name. |
| */ |
| public DOMRecord(String rootElementName) { |
| this(rootElementName, (NamespaceResolver)null); |
| } |
| |
| /** |
| * INTERNAL: |
| * Create a record with the root element name get the namespace URI from the namespaceResolver. |
| */ |
| public DOMRecord(String rootElementName, NamespaceResolver namespaceResolver) { |
| this(); |
| String rootElementNamespaceURI = resolveNamespace(namespaceResolver, rootElementName); |
| setDOM(createNewDocument(rootElementName, rootElementNamespaceURI)); |
| } |
| |
| /** |
| * INTERNAL: |
| * Create a record with the root element name get the namespace URI from the namespaceResolver. |
| */ |
| public DOMRecord(String rootElementName, String rootElementNamespaceURI) { |
| this(); |
| setDOM(createNewDocument(rootElementName, rootElementNamespaceURI)); |
| } |
| |
| /** |
| * INTERNAL: |
| * Create a record with the local root element name, that is a child of the parent. |
| */ |
| public DOMRecord(String localRootElementName, Node parent) { |
| this(localRootElementName, null, parent); |
| } |
| |
| /** |
| * INTERNAL: |
| * Create a record with the local root element name, that is a child of the parent. |
| * Lookup the namespace URI from the namespaceResolver. |
| */ |
| public DOMRecord(String localRootElementName, NamespaceResolver namespaceResolver, Node parent) { |
| this(); |
| Document document; |
| if (parent instanceof Document) { |
| document = (Document)parent; |
| } else { |
| document = parent.getOwnerDocument(); |
| } |
| |
| String localRootElementNamespaceURI = resolveNamespace(namespaceResolver, localRootElementName); |
| Element child = document.createElementNS(localRootElementNamespaceURI, localRootElementName); |
| parent.appendChild(child); |
| setDOM(child); |
| } |
| |
| /** |
| * INTERNAL: |
| * Create a record with the element. |
| */ |
| public DOMRecord(Element element) { |
| this(); |
| setDOM(element); |
| } |
| |
| public DOMRecord(Node node) { |
| this(); |
| setDOM(node); |
| } |
| |
| /** |
| * INTERNAL: |
| * Create a record with the element. |
| */ |
| public DOMRecord(Document document) { |
| this(); |
| setDOM(document.getDocumentElement()); |
| } |
| |
| /** |
| * PUBLIC: |
| * Get the local name of the context root element. |
| */ |
| @Override |
| public String getLocalName() { |
| String localName = getDOM().getLocalName(); |
| if (null != localName) { |
| return localName; |
| } |
| return getDOM().getNodeName(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Get the namespace URI for the context root element. |
| */ |
| @Override |
| public String getNamespaceURI() { |
| return getDOM().getNamespaceURI(); |
| } |
| |
| /** |
| * INTERNAL: |
| * The ReferenceResolver that is leveraged by key based mappings. |
| * @since EclipseLink 2.5.0 |
| */ |
| public ReferenceResolver getReferenceResolver() { |
| if(null == referenceResolver) { |
| referenceResolver = new ReferenceResolver(); |
| } |
| return referenceResolver; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the ReferenceResolver that will be leveraged by key based mappings. |
| * @since EclipseLink 2.5.0 |
| */ |
| public void setReferenceResolver(ReferenceResolver referenceResolver) { |
| this.referenceResolver = referenceResolver; |
| } |
| |
| /** |
| * INTERNAL: |
| * Add the field-value pair to the document. |
| */ |
| @Override |
| public void add(DatabaseField key, Object value) { |
| // Value may be a direct value, nested record, or collection of values. |
| Object nodeValue = convertToNodeValue(value); |
| XPathEngine.getInstance().create(convertToXMLField(key), dom, nodeValue, session); |
| } |
| |
| /** |
| * INTERNAL: |
| * Convert the value which may be a direct value, nested record, or set of nested records, |
| * to a node value usable with the XPathEngine. |
| */ |
| private Object convertToNodeValue(Object value) { |
| if (value instanceof List) { |
| List values = (List)value; |
| Vector nodeValues = new Vector(values.size()); |
| for (int index = 0; index < values.size(); index++) { |
| Object nestedValue = values.get(index); |
| nodeValues.add(convertToNodeValue(nestedValue)); |
| } |
| return nodeValues; |
| } else if (value instanceof DOMRecord) { |
| return ((DOMRecord)value).getDOM(); |
| } else if (value != null && value.getClass() == XMLEntry.class) { |
| XMLEntry entry = (XMLEntry)value; |
| entry.setValue(convertToNodeValue(entry.getValue())); |
| return entry; |
| } else { |
| return value; |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Clear the sub-nodes of the DOM. |
| */ |
| @Override |
| public void clear() { |
| if(getDOM() instanceof Element) { |
| String domName = ((Element)getDOM()).getTagName(); |
| this.dom = createNewDocument(domName, null); |
| this.currentNode = this.dom; |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Clone the row and its values. |
| */ |
| @Override |
| public DOMRecord clone() { |
| DOMRecord clone = (DOMRecord)super.clone(); |
| if (clone != null) { |
| clone.setDOM((Element)dom.cloneNode(true)); |
| } |
| return clone; |
| } |
| |
| /** |
| * INTERNAL: |
| * Creates a new Document and returns the root element of that document |
| */ |
| public Node createNewDocument(String defaultRootElementName) { |
| return createNewDocument(defaultRootElementName, null); |
| } |
| |
| /** |
| * INTERNAL: |
| * Creates a new Document and returns the root element of that document |
| */ |
| public Node createNewDocument(String defaultRootElementName, String namespaceURI) { |
| XMLPlatform xmlPlatform = XMLPlatformFactory.getInstance().getXMLPlatform(); |
| Document document = xmlPlatform.createDocument(); |
| |
| if (defaultRootElementName == null || defaultRootElementName.length() == 0) { |
| DocumentFragment fragment = document.createDocumentFragment(); |
| return fragment; |
| } else { |
| Node rootNode = document.createElementNS(namespaceURI, defaultRootElementName); |
| document.appendChild(rootNode); |
| return document.getDocumentElement(); |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the document. |
| */ |
| @Override |
| public Document getDocument() { |
| return getDOM().getOwnerDocument(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Check if the field is contained in the row. |
| */ |
| @Override |
| public boolean containsKey(DatabaseField key) { |
| XMLField xmlField = convertToXMLField(key); |
| NodeList nodeList = UnmarshalXPathEngine.getInstance().selectNodes(dom, xmlField, xmlField.getNamespaceResolver()); |
| return nodeList.getLength() > 0; |
| } |
| |
| /** |
| * PUBLIC: |
| * Check if the value is contained in the row. |
| */ |
| @Override |
| public boolean contains(Object value) { |
| return values().contains(value); |
| } |
| |
| @Override |
| public Object get(DatabaseField key) { |
| Object value = getIndicatingNoEntry(key); |
| if(value == noEntry) { |
| return null; |
| } |
| return value; |
| } |
| /** |
| * INTERNAL: |
| * Given a DatabaseField return the corresponding value from the document |
| */ |
| @Override |
| public Object getIndicatingNoEntry(DatabaseField key) { |
| return getIndicatingNoEntry(key, false, false); |
| } |
| |
| public Object getIndicatingNoEntry(DatabaseField key, boolean shouldReturnNode) { |
| return getIndicatingNoEntry(key, shouldReturnNode, false); |
| } |
| |
| public Object getIndicatingNoEntry(DatabaseField key, boolean shouldReturnNode, boolean checkForXsiNil) { |
| XMLField field = convertToXMLField(key); |
| |
| // handle 'self' xpath |
| if (field.isSelfField()) { |
| return this; |
| } |
| |
| Object result = UnmarshalXPathEngine.getInstance().selectSingleNode(dom, field, field.getNamespaceResolver(), checkForXsiNil); |
| |
| if(result == noEntry) { |
| if(shouldReturnNode) { |
| return null; |
| } |
| return noEntry; |
| } |
| |
| if (result == NIL) { |
| return NIL; |
| } |
| |
| Node node = (Node)result; |
| if(shouldReturnNode) { |
| return node; |
| } |
| // If a node was not found return null |
| if (null == node) { |
| return null; |
| } |
| |
| // For Attributes and Text nodes return their value |
| if (Node.ELEMENT_NODE != node.getNodeType()) { |
| if (node.getNodeType() == Node.ATTRIBUTE_NODE) { |
| getValueFromAttribute((Attr)node, field); |
| } |
| // For Text, must handle typed elements |
| return getValueFromElement((Element)node.getParentNode(), node, field); |
| } |
| |
| // If an element was found |
| return buildNestedRow((Element)node); |
| } |
| |
| /** |
| * INTERNAL: |
| * Retrieve the value for the field name. |
| */ |
| @Override |
| public Object getValues(String key) { |
| Object value = getValuesIndicatingNoEntry(new XMLField(key)); |
| |
| if (value == AbstractRecord.noEntry) { |
| return null; |
| } |
| |
| return value; |
| } |
| |
| /** |
| * INTERNAL: |
| * Given a DatabaseField, return the corresponding values from the document |
| */ |
| @Override |
| public Object getValues(DatabaseField key) { |
| return this.getValues(key, null); |
| } |
| |
| public Object getValues(DatabaseField key, AbstractNullPolicy nullPolicy) { |
| Object value = getValuesIndicatingNoEntry(key, nullPolicy); |
| |
| if (value == AbstractRecord.noEntry) { |
| return null; |
| } |
| |
| return value; |
| } |
| |
| public Object getValuesIndicatingNoEntry(DatabaseField key) { |
| return this.getValuesIndicatingNoEntry(key, null); |
| } |
| |
| public Object getValuesIndicatingNoEntry(DatabaseField key, AbstractNullPolicy nullPolicy) { |
| return getValuesIndicatingNoEntry(key, false, nullPolicy); |
| } |
| |
| public List<XMLEntry> getValuesIndicatingNoEntry(List<DatabaseField> keys) { |
| return getValuesIndicatingNoEntry(keys, false); |
| } |
| |
| public List<XMLEntry> getValuesIndicatingNoEntry(List<DatabaseField> keys, boolean shouldReturnNodes) { |
| List<XMLField> xmlFields = convertToXMLField(keys); |
| List<XMLEntry> values = UnmarshalXPathEngine.getInstance().selectNodes(dom, xmlFields, xmlFields.get(0).getNamespaceResolver()); |
| if(shouldReturnNodes) { |
| return values; |
| } |
| for(XMLEntry next:values) { |
| Node nextNode = (Node)next.getValue(); |
| if(!(nextNode.getNodeType() == Node.ELEMENT_NODE)) { |
| Object value = getValueFromElement((Element)nextNode.getParentNode(), nextNode, next.getXMLField()); |
| next.setValue(value); |
| } else { |
| next.setValue(buildNestedRow((Element)nextNode)); |
| } |
| } |
| return values; |
| } |
| |
| /** |
| * INTERNAL: |
| * Given a DatabaseField, return the corresponding values from the document |
| */ |
| public Object getValuesIndicatingNoEntry(DatabaseField key, boolean shouldReturnNodes) { |
| return this.getValuesIndicatingNoEntry(key, shouldReturnNodes, null); |
| } |
| |
| public Object getValuesIndicatingNoEntry(DatabaseField key, boolean shouldReturnNodes, AbstractNullPolicy nullPolicy) { |
| XMLField field = convertToXMLField(key); |
| NodeList nodeList = UnmarshalXPathEngine.getInstance().selectNodes(dom, field, field.getNamespaceResolver(), nullPolicy); |
| |
| // If a node was not found return null |
| if (null == nodeList) { |
| return null; |
| } |
| int resultSize = nodeList.getLength(); |
| Vector result = new Vector(resultSize); |
| if (resultSize == 0) { |
| return result; |
| } |
| if(shouldReturnNodes) { |
| //just copy all the nodes into the result vector and return it |
| for(int i = 0; i < resultSize; i++) { |
| result.add(nodeList.item(i)); |
| } |
| return result; |
| } |
| // Assumption: NodeList contains nodes of the same type |
| Node firstNode = nodeList.item(0); |
| if ((firstNode == null) || (firstNode.getNodeType() != Node.ELEMENT_NODE)) { |
| if (field.usesSingleNode() && (resultSize == 1)) { |
| Node next = nodeList.item(0); |
| if (next == null) { |
| result.add(null); |
| } else { |
| Vector list = new Vector(); |
| String sourceObject = next.getNodeValue(); |
| |
| StringTokenizer tokenizer = new StringTokenizer(sourceObject, " "); |
| while (tokenizer.hasMoreElements()) { |
| String token = tokenizer.nextToken(); |
| Object nextItem = convertValue((Element)next.getParentNode(), field, token); |
| list.add(nextItem); |
| } |
| return list; |
| } |
| } |
| for (int x = 0; x < resultSize; x++) { |
| Node next = nodeList.item(x); |
| if (next == null) { |
| result.add(null); |
| } else { |
| result.add(getValueFromElement((Element)next.getParentNode(), next, field)); |
| } |
| } |
| } else { |
| for (int x = 0; x < resultSize; x++) { |
| result.add(buildNestedRow((Element)nodeList.item(x))); |
| } |
| } |
| |
| return result; |
| } |
| |
| private Object getValueFromAttribute(Attr node, XMLField key) { |
| currentNode = node.getOwnerElement(); |
| Object convertedValue = key.convertValueBasedOnSchemaType(node.getNodeValue(), (XMLConversionManager) session.getDatasourcePlatform().getConversionManager(), this); |
| currentNode = getDOM(); |
| return convertedValue; |
| |
| } |
| |
| private Object getValueFromElement(Element node, Node textChild, Field key) { |
| Object value = textChild.getNodeValue(); |
| return convertValue(node, key, value); |
| } |
| |
| private Object convertValue(Element node, Field key, Object value) { |
| XMLConversionManager xmlCnvMgr = (XMLConversionManager) session.getDatasourcePlatform().getConversionManager(); |
| if (key.isTypedTextField() && (node != null)) { |
| String schemaType = node.getAttributeNS(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, XMLConstants.SCHEMA_TYPE_ATTRIBUTE); |
| if ((null != schemaType) && (schemaType.length() > 0)) { |
| QName qname = null; |
| int index = schemaType.indexOf(XMLConstants.COLON); |
| if (index == -1) { |
| qname = new QName(schemaType); |
| Class convertClass = key.getJavaClass(qname, xmlCnvMgr); |
| return xmlCnvMgr.convertObject(value, convertClass); |
| } else { |
| String prefix = schemaType.substring(0, index); |
| String localPart = schemaType.substring(index + 1); |
| XMLPlatform xmlPlatform = XMLPlatformFactory.getInstance().getXMLPlatform(); |
| String url = xmlPlatform.resolveNamespacePrefix(node, prefix); |
| qname = new QName(url, localPart); |
| Class convertClass = key.getJavaClass(qname, xmlCnvMgr); |
| return xmlCnvMgr.convertObject(value, convertClass, qname); |
| } |
| } |
| } |
| currentNode = node; |
| Object convertedValue = key.convertValueBasedOnSchemaType(value, xmlCnvMgr, this); |
| currentNode = getDOM(); |
| return convertedValue; |
| } |
| |
| /** |
| * INTERNAL: |
| * Build the nested record, this can be overwriten by subclasses to use their subclass instance. |
| */ |
| public XMLRecord buildNestedRow(Element element) { |
| DOMRecord record = new DOMRecord(element); |
| record.setUnmarshaller(this.getUnmarshaller()); |
| record.setOwningObject(this.getCurrentObject()); |
| record.setDocPresPolicy(this.getDocPresPolicy()); |
| record.setReferenceResolver(referenceResolver); |
| return record; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the DOM. |
| */ |
| @Override |
| public Node getDOM() { |
| return dom; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the field value into the DOM. |
| * The field name must be a valid simple XPath expression. |
| */ |
| @Override |
| public Object put(DatabaseField key, Object value) { |
| // Value may be a direct value, nested record, or collection of values. |
| XMLField field = convertToXMLField(key); |
| Object nodeValue = convertToNodeValue(value); |
| NodeList replaced = null; |
| boolean isEmptyCollection = false; |
| if (nodeValue instanceof Collection) { |
| isEmptyCollection = ((Collection)nodeValue).size() == 0; |
| replaced = XPathEngine.getInstance().replaceCollection(convertToXMLField(key), dom, (Collection)nodeValue, session); |
| } else { |
| replaced = XPathEngine.getInstance().replaceValue(convertToXMLField(key), dom, nodeValue, session); |
| } |
| if (replaced.getLength() == 0) { |
| // Replace does nothing if the node did not exist, return no nodes. |
| XPathEngine.getInstance().create(convertToXMLField(key), dom, nodeValue, lastUpdatedField, getDocPresPolicy(), session); |
| } else if (replaced.item(0) == getDOM()) { |
| // If the root element/record element was changed must update the record's reference. |
| setDOM(getDocument().getDocumentElement()); |
| } |
| if(!field.getXPathFragment().isAttribute() && !field.getXPathFragment().nameIsText()) { |
| if(value != null && !isEmptyCollection) { |
| this.lastUpdatedField = field; |
| } |
| } |
| return replaced; |
| } |
| |
| public Object put(List<XMLField> xmlFields, List<XMLEntry> values) { |
| Vector valuesToWrite = (Vector)convertToNodeValue(values); |
| List<XMLEntry> replaced = null; |
| |
| replaced = XPathEngine.getInstance().replaceCollection(xmlFields, valuesToWrite, dom, getDocPresPolicy(), lastUpdatedField, session); |
| if(replaced.size() == 0) { |
| XPathEngine.getInstance().create(xmlFields, dom, valuesToWrite, lastUpdatedField, getDocPresPolicy(), session); |
| } |
| return replaced; |
| } |
| |
| @Override |
| public Object put(Object key, Object value) throws ValidationException { |
| if (key instanceof String) { |
| return put((String)key, value); |
| } else if (key instanceof DatabaseField) { |
| return put((DatabaseField)key, value); |
| } else if (key instanceof List) { |
| return put((List<XMLField>)key, (List<XMLEntry>)value); |
| } else { |
| throw ValidationException.onlyFieldsAreValidKeysForDatabaseRows(); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Remove the field key from the row. |
| */ |
| @Override |
| public Object remove(DatabaseField key) { |
| return XPathEngine.getInstance().remove(convertToXMLField(key), dom, true); |
| } |
| |
| /** |
| * INTERNAL: |
| * replaces the value at index with value |
| */ |
| @Override |
| public void replaceAt(Object value, int index) { |
| throw XMLMarshalException.operationNotSupported("replaceAt(Object value, int index)"); |
| } |
| |
| /** |
| * PUBLIC: |
| */ |
| @Override |
| public Set entrySet() { |
| int size = this.size(); |
| Map tempMap = new HashMap(size); |
| Vector fields = getFields(); |
| Vector values = getValues(); |
| for (int i = 0; i < size; i++) { |
| tempMap.put(fields.elementAt(i), values.elementAt(i)); |
| } |
| return tempMap.entrySet(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Setting fields vector will not update the document so this is not supported |
| */ |
| @Override |
| protected void setFields(Vector fields) throws XMLMarshalException { |
| throw XMLMarshalException.operationNotSupported("setField(Vector fields)"); |
| } |
| |
| /** |
| * INTERNAL: |
| * This should not be used, but added some support for it as |
| * is called from some places such as sdk call used in the descriptor to define operation not supported, |
| * may also be called from toplin in some places. |
| */ |
| @Override |
| public Vector getFields() { |
| int length = getDOM().getChildNodes().getLength(); |
| Node nextNode = null; |
| if(length > 0) { |
| nextNode = getDOM().getChildNodes().item(0); |
| } |
| Vector fields = new Vector(length); |
| while(nextNode != null) { |
| fields.add(new DatabaseField(nextNode.getNodeName())); |
| nextNode = nextNode.getNextSibling(); |
| } |
| return fields; |
| } |
| |
| /** |
| * INTERNAL: |
| * This should not be used, but added some support for it as |
| * is called from some places such as sdk call used in the descriptor to define operation not supported, |
| * may also be called from TopLink in some places. |
| */ |
| @Override |
| public Vector getValues() { |
| int length = getDOM().getChildNodes().getLength(); |
| Node nextNode = null; |
| if(length > 0) { |
| nextNode = getDOM().getFirstChild(); |
| } |
| Vector values = new Vector(length); |
| while(nextNode != null) { |
| values.add(nextNode); |
| nextNode = nextNode.getNextSibling(); |
| } |
| return values; |
| } |
| |
| /** |
| * INTERNAL: |
| * Setting values vector will not update the document so this is not supported |
| */ |
| @Override |
| protected void setValues(Vector values) throws XMLMarshalException { |
| throw XMLMarshalException.operationNotSupported("setValues(Vector values)"); |
| } |
| |
| /** |
| * INTERNAL: |
| * Sets the dom and updated document to be the owner document of the given element |
| */ |
| public void setDOM(Node element) { |
| this.dom = element; |
| this.currentNode = element; |
| this.getNamespaceResolver().setDOM(element); |
| } |
| |
| public void setDOM(Element element) { |
| this.dom = element; |
| this.currentNode = element; |
| this.getNamespaceResolver().setDOM(element); |
| } |
| |
| /** |
| * INTERNAL: |
| * Print the dom XML string. |
| */ |
| @Override |
| public String toString() { |
| StringWriter writer = new StringWriter(); |
| writer.write(Helper.getShortClassName(getClass())); |
| writer.write("("); |
| transformToWriter(writer); |
| writer.write(")"); |
| return writer.toString(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the set of element names from the DOM. |
| */ |
| @Override |
| public Set keySet() { |
| int length = getDOM().getChildNodes().getLength(); |
| HashSet keys = new HashSet(length); |
| for (int index = 0; index < length; index++) { |
| keys.add(getDOM().getChildNodes().item(index).getNodeName()); |
| } |
| return keys; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the collection of element values from the DOM. |
| */ |
| @Override |
| public Collection values() { |
| int length = getDOM().getChildNodes().getLength(); |
| Vector values = new Vector(length); |
| for (int index = 0; index < length; index++) { |
| values.add(getDOM().getChildNodes().item(index)); |
| } |
| return values; |
| } |
| |
| /** |
| * Return the number of elements in the DOM. |
| */ |
| @Override |
| public int size() { |
| return getDOM().getAttributes().getLength() + getDOM().getChildNodes().getLength(); |
| } |
| |
| /** |
| * Set the XML from an XML string. |
| */ |
| public void transformFromXML(String xml) { |
| Reader reader = new StringReader(xml); |
| transformFromXML(reader); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the namespace uri for the prefix of the given local name |
| */ |
| private String resolveNamespace(NamespaceResolver namespaceResolver, String localName) { |
| if(localName == null) { |
| return null; |
| } |
| int colonIndex = localName.indexOf(XMLConstants.COLON); |
| if (colonIndex < 0) { |
| // handle target/default namespace |
| if (namespaceResolver != null) { |
| return namespaceResolver.getDefaultNamespaceURI(); |
| } |
| return null; |
| } else { |
| if (namespaceResolver == null) { |
| //throw an exception if the name has a : in it but the namespaceresolver is null |
| throw XMLMarshalException.namespaceResolverNotSpecified(localName); |
| } |
| String prefix = localName.substring(0, colonIndex); |
| String uri = namespaceResolver.resolveNamespacePrefix(prefix); |
| if (uri == null) { |
| //throw an exception if the prefix is not found in the namespaceresolver |
| throw XMLMarshalException.namespaceNotFound(prefix); |
| } |
| return uri; |
| } |
| } |
| |
| @Override |
| public void setSession(AbstractSession session) { |
| this.session = session; |
| if (session != null && session.getDatasourceLogin() instanceof XMLLogin) { |
| this.equalNamespaceResolvers = ((XMLLogin) session.getDatasourceLogin()).hasEqualNamespaceResolvers(); |
| } |
| } |
| |
| /** |
| * Set the XML from an XML reader. |
| */ |
| public void transformFromXML(Reader reader) { |
| XMLParser parser = XMLPlatformFactory.getInstance().getXMLPlatform().newXMLParser(); |
| Document document = parser.parse(reader); |
| setDOM(document.getDocumentElement()); |
| } |
| |
| /** |
| * Return the XML string representation of the DOM. |
| */ |
| @Override |
| public String transformToXML() { |
| StringWriter writer = new StringWriter(); |
| transformToWriter(writer); |
| return writer.toString(); |
| } |
| |
| /** |
| * Write the XML string representation of the DOM. |
| */ |
| public void transformToWriter(Writer writer) { |
| XMLTransformer xmlTransformer = XMLPlatformFactory.getInstance().getXMLPlatform().newXMLTransformer(); |
| xmlTransformer.transform(this.getDOM(), writer); |
| } |
| |
| @Override |
| public String resolveNamespacePrefix(String prefix) { |
| XMLPlatform xmlPlatform = XMLPlatformFactory.getInstance().getXMLPlatform(); |
| return xmlPlatform.resolveNamespacePrefix(currentNode, prefix); |
| } |
| |
| /** |
| * INTERNAL: |
| * If the UnmarshalRecord has a ReferenceResolver, tell it to resolve its |
| * references. |
| * @since EclipseLink 2.5.0 |
| */ |
| public void resolveReferences(CoreAbstractSession abstractSession, IDResolver idResolver) { |
| if(null != referenceResolver) { |
| referenceResolver.resolveReferences(abstractSession, idResolver, unmarshaller.getErrorHandler()); |
| } |
| } |
| |
| } |