org.eclipse.persistence.internal.oxm.record.DOMReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.eclipse.persistence.core Show documentation
Show all versions of org.eclipse.persistence.core Show documentation
EclipseLink build based upon Git transaction ecdf3c32c4
/*
* 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.internal.oxm.record;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.persistence.internal.core.sessions.CoreAbstractSession;
import org.eclipse.persistence.internal.oxm.Constants;
import org.eclipse.persistence.internal.oxm.NamespaceResolver;
import org.eclipse.persistence.internal.oxm.Unmarshaller;
import org.eclipse.persistence.internal.oxm.mappings.Login;
import org.eclipse.persistence.internal.oxm.mappings.Mapping;
import org.eclipse.persistence.internal.oxm.record.namespaces.StackUnmarshalNamespaceResolver;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.oxm.documentpreservation.DocumentPreservationPolicy;
import org.w3c.dom.Attr;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.ext.Locator2;
/**
* INTERNAL:
* Purpose: An implementation of XMLReader for parsing DOM Nodes into SAX events.
*
Responsibilities:
* - Walk the DOM tree and report sax events to the provided content handler
* - Report lexical events to the lexical handler if it's provided
* - Listen for callbacks from the Mapping-Level framework to handle caching nodes for document preservation
*
*
*/
public class DOMReader extends XMLReaderAdapter {
private Node currentNode;
private DocumentPreservationPolicy docPresPolicy;
public DOMReader() {
super();
}
public DOMReader(Unmarshaller xmlUnmarshaller) {
super(xmlUnmarshaller);
}
@Override
public void parse(InputSource input) throws SAXException {
if(input instanceof DOMInputSource) {
Node node = ((DOMInputSource) input).getNode();
if(contentHandler != null && contentHandler.getClass() == SAXUnmarshallerHandler.class){
((SAXUnmarshallerHandler)contentHandler).setUnmarshalNamespaceResolver(new StackUnmarshalNamespaceResolver());
}
parse(node);
}
}
public void parse (Node node, String newURI, String newName) throws SAXException {
if(null == contentHandler) {
return;
}
Element rootNode = null;
if(node.getNodeType() == Node.DOCUMENT_NODE) {
rootNode = ((Document)node).getDocumentElement();
} else {
rootNode = (Element)node;
}
if(rootNode == null) {
return;
}
processParentNamespaces(rootNode);
startDocument();
setupLocator(rootNode.getOwnerDocument());
reportElementEvents(rootNode, newURI, newName);
endDocument();
}
public void parse (Node node) throws SAXException {
parse(node, null, null);
}
/**
* Process namespace declarations on parent elements if not the root.
* For each parent node from current to root push each onto a stack,
* then pop each off, calling startPrefixMapping for each XMLNS
* attribute. Using a stack ensures that the parent nodes are
* processed top down.
*
* @param element
*/
protected void processParentNamespaces(Element element) throws SAXException {
Node parent = element.getParentNode();
// If we're already at the root, do nothing
if (parent != null && parent.getNodeType() == Node.DOCUMENT_NODE) {
return;
}
// Add each parent node up to root to the stack
List parentElements = new ArrayList();
while (parent != null && parent.getNodeType() != Node.DOCUMENT_NODE) {
parentElements.add(parent);
parent = parent.getParentNode();
}
// Pop off each node and call startPrefixMapping for each XMLNS attribute
for (Iterator stackIt = parentElements.iterator(); stackIt.hasNext(); ) {
NamedNodeMap attrs = parentElements.remove(parentElements.size() - 1).getAttributes();
if (attrs != null) {
for (int i=0, length = attrs.getLength(); i < length; i++) {
Attr next = (Attr)attrs.item(i);
String attrPrefix = next.getPrefix();
if (attrPrefix != null && attrPrefix.equals(javax.xml.XMLConstants.XMLNS_ATTRIBUTE)) {
contentHandler.startPrefixMapping(next.getLocalName(), next.getValue());
}
}
}
}
}
protected void reportElementEvents(Element elem) throws SAXException {
reportElementEvents(elem, null, null);
}
protected void reportElementEvents(Element elem, String newUri, String newName) throws SAXException {
this.currentNode = elem;
IndexedAttributeList attributes = buildAttributeList(elem);
String namespaceUri = null;
String qname = null;
String lname = null;
if(newName == null){
// Handle null local name
lname = elem.getLocalName();
if (lname == null) {
// If local name is null, use the node name
lname = elem.getNodeName();
qname = lname;
handlePrefixedAttribute(elem);
} else {
qname = getQName(elem);
}
namespaceUri = elem.getNamespaceURI();
if(namespaceUri == null) {
namespaceUri = "";
}
} else {
namespaceUri = newUri;
lname = newName;
qname = newName;
if(namespaceUri != null && isNamespaceAware()){
NamespaceResolver tmpNR = new NamespaceResolver();
tmpNR.setDOM(elem);
String prefix = tmpNR.resolveNamespaceURI(namespaceUri);
if(prefix == null || prefix.length() == 0){
String defaultNamespace = elem.getAttributeNS(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, javax.xml.XMLConstants.XMLNS_ATTRIBUTE);
if(defaultNamespace == null){
prefix = tmpNR.generatePrefix();
contentHandler.startPrefixMapping(prefix, namespaceUri);
}else if(defaultNamespace != namespaceUri){
prefix = tmpNR.generatePrefix();
contentHandler.startPrefixMapping(prefix, namespaceUri);
}else{
prefix = Constants.EMPTY_STRING;
}
}
if(prefix != null && prefix.length() >0){
qname = prefix + Constants.COLON + qname;
}
}
}
contentHandler.startElement(namespaceUri, lname, qname, attributes);
handleChildNodes(elem.getChildNodes());
contentHandler.endElement(namespaceUri, lname, qname);
endPrefixMappings(elem);
}
protected IndexedAttributeList buildAttributeList(Element elem) throws SAXException {
IndexedAttributeList attributes = new IndexedAttributeList();
NamedNodeMap attrs = elem.getAttributes();
for (int i = 0, length = attrs.getLength(); i < length; i++) {
Attr next = (Attr)attrs.item(i);
String attrPrefix = next.getPrefix();
if(attrPrefix != null && attrPrefix.equals(javax.xml.XMLConstants.XMLNS_ATTRIBUTE)) {
contentHandler.startPrefixMapping(next.getLocalName(), next.getValue());
// Handle XMLNS prefixed attributes
handleNewNamespaceDeclaration(elem, next.getLocalName(), next.getValue());
} else if(attrPrefix == null) {
String name = next.getLocalName();
if(name == null) {
name = next.getNodeName();
}
if(name != null && name.equals(javax.xml.XMLConstants.XMLNS_ATTRIBUTE)) {
contentHandler.startPrefixMapping(Constants.EMPTY_STRING, next.getValue());
handleNewNamespaceDeclaration(elem, Constants.EMPTY_STRING, next.getValue());
}
}
if(next.getNamespaceURI() != null && next.getNamespaceURI().equals(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI) && next.getLocalName().equals("type")) {
handleXsiTypeAttribute(next);
}
attributes.addAttribute(next);
}
return attributes;
}
protected void endPrefixMappings(Element elem) throws SAXException {
NamedNodeMap attrs = elem.getAttributes();
for(int i = 0, numOfAtts = attrs.getLength(); i < numOfAtts; i++) {
Attr next = (Attr)attrs.item(i);
String attrPrefix = next.getPrefix();
if (attrPrefix != null && attrPrefix.equals(javax.xml.XMLConstants.XMLNS_ATTRIBUTE)) {
contentHandler.endPrefixMapping(next.getLocalName());
} else if(attrPrefix == null) {
String name = next.getLocalName();
if(name == null) {
name = next.getNodeName();
}
if(javax.xml.XMLConstants.XMLNS_ATTRIBUTE.equals(name)) {
contentHandler.endPrefixMapping(Constants.EMPTY_STRING);
}
}
}
}
protected String getQName(Element elem) throws SAXException {
handlePrefixedAttribute(elem);
String prefix = elem.getPrefix();
if (prefix != null && prefix.length() > 0) {
String qname = prefix + Constants.COLON + elem.getLocalName();
return qname;
} else {
return elem.getLocalName();
}
}
protected void handleNewNamespaceDeclaration(Element elem, String emptyString, String value) {
// DO NOTHING
}
protected void handleXsiTypeAttribute(Attr attr) throws SAXException {
}
/**
* Handle prefixed attribute - may need to declare the namespace
* URI locally.
*
*/
protected void handlePrefixedAttribute(Element elem) throws SAXException {
// DO NOTHING
}
protected void handleChildNodes(NodeList children) throws SAXException {
Node nextChild = null;
if(children.getLength() > 0) {
nextChild = children.item(0);
}
while(nextChild != null) {
if(nextChild.getNodeType() == Node.TEXT_NODE) {
char[] value = ((Text)nextChild).getNodeValue().toCharArray();
contentHandler.characters(value, 0, value.length);
} else if(nextChild.getNodeType() == Node.COMMENT_NODE) {
char[] value = ((Comment)nextChild).getNodeValue().toCharArray();
if (lexicalHandler != null) {
lexicalHandler.comment(value, 0, value.length);
}
} else if(nextChild.getNodeType() == Node.ELEMENT_NODE) {
Element childElement = (Element)nextChild;
reportElementEvents(childElement);
} else if(nextChild.getNodeType() == Node.CDATA_SECTION_NODE) {
if(lexicalHandler != null) {
lexicalHandler.startCDATA();
}
char[] value = ((CDATASection)nextChild).getData().toCharArray();
contentHandler.characters(value, 0, value.length);
if(lexicalHandler != null) {
lexicalHandler.endCDATA();
}
}
nextChild = nextChild.getNextSibling();
}
}
/**
* Trigger an endDocument event on the contenthandler.
*/
protected void endDocument() throws SAXException {
contentHandler.endDocument();
}
/**
* Trigger a startDocument event on the contenthandler.
*/
protected void startDocument() throws SAXException {
contentHandler.startDocument();
}
/**
* An EclipseLink specific callback into the Reader. This allows Objects to be
* associated with the XML Nodes they came from.
*/
@Override
public void newObjectEvent(Object object, Object parent, Mapping selfRecordMapping) {
docPresPolicy.addObjectToCache(object, currentNode, selfRecordMapping);
}
@Override
public Object getCurrentObject(CoreAbstractSession session, Mapping selfRecordMapping) {
//if session == null then this is a marshal of a non-root
//if docPres policy is null, then we never unmarshalled anything, and can
//safely return null;
if(session == null && docPresPolicy == null) {
return null;
}
if(docPresPolicy == null) {
Login login = (Login)session.getDatasourceLogin();
docPresPolicy = login.getDocumentPreservationPolicy();
}
return docPresPolicy.getObjectForNode(currentNode, selfRecordMapping);
}
public DocumentPreservationPolicy getDocPresPolicy() {
return docPresPolicy;
}
public void setDocPresPolicy(DocumentPreservationPolicy policy) {
docPresPolicy = policy;
}
protected void setupLocator(Document doc) {
LocatorImpl locator = new LocatorImpl();
try {
locator.setEncoding(doc.getXmlEncoding());
locator.setXMLVersion(doc.getXmlVersion());
} catch(Exception ex) {
//if unable to invoke these methods, just return and don't invoke
return;
}
this.contentHandler.setDocumentLocator(locator);
}
// Made static final for performance reasons.
/**
* Implementation of Attributes - used to pass along a given node's attributes
* to the startElement method of the reader's content handler.
*/
protected static final class IndexedAttributeList implements org.xml.sax.Attributes {
private List attrs;
public IndexedAttributeList() {
attrs = new ArrayList();
}
public void addAttribute(Attr attribute) {
attrs.add(attribute);
}
public String getQName(int index) {
try {
Attr item = attrs.get(index);
if (item.getName() != null) {
return item.getName();
}
return Constants.EMPTY_STRING;
} catch (IndexOutOfBoundsException iobe) {
return null;
}
}
public String getType(String namespaceUri, String localName) {
return Constants.CDATA;
}
public String getType(int index) {
return Constants.CDATA;
}
public String getType(String qname) {
return Constants.CDATA;
}
public int getIndex(String qname) {
for (int i=0, size = attrs.size(); i