blob: c4b6c422d9867ee837b79b681b4282124223c762 [file] [log] [blame]
/*
* 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.platform.xml;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.persistence.internal.oxm.Constants;
import org.eclipse.persistence.internal.oxm.StrBuffer;
import org.eclipse.persistence.internal.oxm.record.ExtendedContentHandler;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.ProcessingInstruction;
import org.w3c.dom.Text;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.ext.LexicalHandler;
/**
* <p><b>Purpose</b>: Build a DOM from SAX events.</p>
*/
public class SAXDocumentBuilder implements ExtendedContentHandler, LexicalHandler {
protected Document document;
protected List<Node> nodes;
protected XMLPlatform xmlPlatform;
protected Map namespaceDeclarations;
protected StrBuffer stringBuffer;
protected Locator locator;
public SAXDocumentBuilder() {
super();
nodes = new ArrayList<>();
xmlPlatform = XMLPlatformFactory.getInstance().getXMLPlatform();
stringBuffer = new StrBuffer();
namespaceDeclarations = new HashMap();
}
public Document getDocument() {
return document;
}
public Document getInitializedDocument() throws SAXException {
if (document == null) {
try {
document = xmlPlatform.createDocument();
nodes.add(document);
} catch (Exception e) {
throw new SAXException(e);
}
}
return document;
}
@Override
public void setDocumentLocator(Locator locator) {
this.locator = locator;
}
@Override
public void startDocument() throws SAXException {
try {
document = xmlPlatform.createDocument();
nodes.add(document);
} catch (Exception e) {
throw new SAXException(e);
}
}
@Override
public void endDocument() throws SAXException {
nodes.remove(nodes.size() - 1);
}
@Override
public void startPrefixMapping(String prefix, String uri) throws SAXException {
if(null == prefix) {
prefix = Constants.EMPTY_STRING;
}
if(null == uri) {
uri = Constants.EMPTY_STRING;
}
if (namespaceDeclarations == null) {
namespaceDeclarations = new HashMap();
}
namespaceDeclarations.put(prefix, uri);
}
@Override
public void endPrefixMapping(String prefix) throws SAXException {
}
@Override
public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
if (null != namespaceURI && namespaceURI.length() == 0) {
namespaceURI = null;
}
Element element = getInitializedDocument().createElementNS(namespaceURI, qName);
Node parentNode = nodes.get(nodes.size()-1);
if ((stringBuffer.length() > 0) && !(nodes.size() == 1)) {
Text text = getInitializedDocument().createTextNode(stringBuffer.toString());
parentNode.appendChild(text);
stringBuffer.reset();
}
appendChildNode(parentNode, element);
nodes.add(element);
if (namespaceDeclarations != null) {
Iterator<Entry<String, String>> namespaceEntries = namespaceDeclarations.entrySet().iterator();
String prefix;
String uri;
while (namespaceEntries.hasNext()) {
Entry<String, String> nextEntry = namespaceEntries.next();
prefix = nextEntry.getKey();
uri = nextEntry.getValue();
boolean prefixEmpty = prefix.length() == 0;
String elemNamespaceURI = element.getNamespaceURI();
boolean elementNamespaceNull = elemNamespaceURI == null;
boolean elementNamespaceEmpty = elemNamespaceURI != null && elemNamespaceURI.length() == 0;
boolean isRootElement = element.getParentNode().getNodeType() == Node.DOCUMENT_NODE;
if (prefixEmpty && isRootElement && (elementNamespaceEmpty || elementNamespaceNull)) {
// Don't add namespace
} else {
addNamespaceDeclaration(element, prefix, uri);
}
}
namespaceDeclarations = null;
}
int numberOfAttributes = atts.getLength();
String attributeNamespaceURI;
for (int x = 0; x < numberOfAttributes; x++) {
attributeNamespaceURI = atts.getURI(x);
if (null != attributeNamespaceURI && attributeNamespaceURI.length() == 0) {
attributeNamespaceURI = null;
}
// Handle case where prefix/uri are not set on an xmlns prefixed attribute
if (attributeNamespaceURI == null && (atts.getQName(x).startsWith(javax.xml.XMLConstants.XMLNS_ATTRIBUTE + ":") || atts.getQName(x).equals(javax.xml.XMLConstants.XMLNS_ATTRIBUTE))) {
attributeNamespaceURI = javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
}
String value = atts.getValue(x);
element.setAttributeNS(attributeNamespaceURI, atts.getQName(x), value);
}
}
@Override
public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
Element endedElement = (Element)nodes.remove(nodes.size()-1);
if (stringBuffer.length() > 0) {
Text text = getInitializedDocument().createTextNode(stringBuffer.toString());
endedElement.appendChild(text);
stringBuffer.reset();
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
stringBuffer.append(ch, start, length);
}
@Override
public void characters(CharSequence characters) {
stringBuffer.append(characters.toString());
}
@Override
public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
}
@Override
public void processingInstruction(String target, String data) throws SAXException {
ProcessingInstruction pi = getInitializedDocument().createProcessingInstruction(target, data);
Node parentNode = nodes.get(nodes.size() -1);
parentNode.appendChild(pi);
}
@Override
public void skippedEntity(String name) throws SAXException {
}
protected void addNamespaceDeclaration(Element parentElement, String prefix, String uri) {
if (prefix.length() == 0 || javax.xml.XMLConstants.XMLNS_ATTRIBUTE.equals(prefix)) {
//handle default/target namespaces
parentElement.setAttributeNS(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, javax.xml.XMLConstants.XMLNS_ATTRIBUTE, uri);
} else {
parentElement.setAttributeNS(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, javax.xml.XMLConstants.XMLNS_ATTRIBUTE + Constants.COLON + prefix, uri);
}
}
public void appendChildNode(Node parentNode, Node childNode) {
parentNode.appendChild(childNode);
}
@Override
public void setNil(boolean isNil) {}
@Override
public void startDTD(String name, String publicId, String systemId) throws SAXException {}
@Override
public void endDTD() throws SAXException {}
@Override
public void startEntity(String name) throws SAXException {}
@Override
public void endEntity(String name) throws SAXException {}
@Override
public void startCDATA() throws SAXException {
CDATASection cdata = document.createCDATASection(null);
Node parentNode = nodes.get(nodes.size() -1);
parentNode.appendChild(cdata);
}
@Override
public void endCDATA() throws SAXException {
CDATASection cdata = (CDATASection)nodes.get(nodes.size()-1).getFirstChild();
if (stringBuffer.length() > 0) {
cdata.setData(stringBuffer.toString());
stringBuffer.reset();
}
}
@Override
public void comment(char[] ch, int start, int length) throws SAXException {}
}