blob: f54aa3469bafa257faf9c524aa878f8fcf0e9aee [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 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.jaxp;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathException;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import org.eclipse.persistence.internal.helper.XMLHelper;
import org.eclipse.persistence.internal.oxm.Constants;
import org.eclipse.persistence.platform.xml.XMLNamespaceResolver;
import org.eclipse.persistence.platform.xml.XMLParser;
import org.eclipse.persistence.platform.xml.XMLPlatform;
import org.eclipse.persistence.platform.xml.XMLPlatformException;
import org.eclipse.persistence.platform.xml.XMLPlatformFactory;
import org.eclipse.persistence.platform.xml.XMLTransformer;
import org.w3c.dom.Attr;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
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.ErrorHandler;
import org.xml.sax.SAXException;
/**
* <p><b>Purpose</b>: An implementation of XMLPlatform using JAXP 1.3 APIs.</p>
*/
public class JAXPPlatform implements XMLPlatform {
private XPathFactory xPathFactory;
private SchemaFactory schemaFactory;
private DocumentBuilderFactory documentBuilderFactory;
private boolean disableSecureProcessing = false;
public JAXPPlatform() {
super();
}
private DocumentBuilderFactory getDocumentBuilderFactory() {
if(null == documentBuilderFactory) {
documentBuilderFactory = XMLHelper.createDocumentBuilderFactory(isSecureProcessingDisabled());
}
return documentBuilderFactory;
}
public XPathFactory getXPathFactory() {
if(null == xPathFactory) {
xPathFactory = XMLHelper.createXPathFactory(isSecureProcessingDisabled());
}
return xPathFactory;
}
public SchemaFactory getSchemaFactory() {
if(null == schemaFactory) {
schemaFactory = XMLHelper.createSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI, isSecureProcessingDisabled());
}
return schemaFactory;
}
/**
* Execute advanced XPath statements that are required for TopLink EIS.
* @param contextNode the node relative to which the XPath
* statement will be executed.
* xPath the XPath statement
* namespaceResolver used to resolve namespace prefixes
* to the corresponding namespace URI
* @return the XPath result
*/
@Override
public NodeList selectNodesAdvanced(Node contextNode, String xPathString, XMLNamespaceResolver xmlNamespaceResolver) throws XMLPlatformException {
try {
XPath xPath = getXPathFactory().newXPath();
if(null != xmlNamespaceResolver) {
JAXPNamespaceContext namespaceContext = new JAXPNamespaceContext(xmlNamespaceResolver);
xPath.setNamespaceContext(namespaceContext);
}
XPathExpression xPathExpression = xPath.compile(xPathString);
return (NodeList) xPathExpression.evaluate(contextNode, XPathConstants.NODESET);
} catch(XPathException e) {
throw XMLPlatformException.xmlPlatformInvalidXPath(e);
}
}
/**
* Execute advanced XPath statements that are required for TopLink EIS.
*/
@Override
public Node selectSingleNodeAdvanced(Node contextNode, String xPathString, XMLNamespaceResolver xmlNamespaceResolver) throws XMLPlatformException {
try {
XPath xPath = getXPathFactory().newXPath();
if(null != xmlNamespaceResolver) {
JAXPNamespaceContext namespaceContext = new JAXPNamespaceContext(xmlNamespaceResolver);
xPath.setNamespaceContext(namespaceContext);
}
XPathExpression xPathExpression = xPath.compile(xPathString);
return (Node) xPathExpression.evaluate(contextNode, XPathConstants.NODE);
} catch(XPathException e) {
throw XMLPlatformException.xmlPlatformInvalidXPath(e);
}
}
@Override
public boolean isWhitespaceNode(Text text) {
String value = text.getNodeValue();
if (null == value) {
return false;
} else {
return value.trim().equals("");
}
}
@Override
public XMLParser newXMLParser() {
return new JAXPParser();
}
@Override
public XMLParser newXMLParser(Map<String, Boolean> parserFeatures) {
return new JAXPParser(parserFeatures);
}
@Override
public XMLTransformer newXMLTransformer() {
return new JAXPTransformer();
}
@Override
public Document createDocument() throws XMLPlatformException {
try {
DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder();
return documentBuilder.newDocument();
} catch (Exception e) {
throw XMLPlatformException.xmlPlatformCouldNotCreateDocument(e);
}
}
@Override
public Document createDocumentWithPublicIdentifier(String name, String publicIdentifier, String systemIdentifier) throws XMLPlatformException {
try {
if (null == publicIdentifier) {
return createDocumentWithSystemIdentifier(name, systemIdentifier);
}
DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder();
DOMImplementation domImpl = documentBuilder.getDOMImplementation();
DocumentType docType = domImpl.createDocumentType(name, publicIdentifier, systemIdentifier);
Document document = domImpl.createDocument(null, name, docType);
return document;
} catch (Exception e) {
throw XMLPlatformException.xmlPlatformCouldNotCreateDocument(e);
}
}
@Override
public Document createDocumentWithSystemIdentifier(String name, String systemIdentifier) throws XMLPlatformException {
try {
Document document = null;
if (null == systemIdentifier) {
document = createDocument();
Element rootElement = document.createElement(name);
document.appendChild(rootElement);
return document;
}
DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder();
DOMImplementation domImpl = documentBuilder.getDOMImplementation();
DocumentType docType = domImpl.createDocumentType(name, null, systemIdentifier);
document = domImpl.createDocument(null, name, docType);
return document;
} catch (Exception e) {
throw XMLPlatformException.xmlPlatformCouldNotCreateDocument(e);
}
}
@Override
public String resolveNamespacePrefix(Node contextNode, String namespacePrefix) throws XMLPlatformException {
if (null == namespacePrefix) {
if (null == contextNode.getPrefix()) {
return contextNode.getNamespaceURI();
}
} else if (namespacePrefix.equals(contextNode.getPrefix())) {
return contextNode.getNamespaceURI();
}
if (contextNode.getNodeType() == Node.ELEMENT_NODE) {
Element contextElement = (Element)contextNode;
Attr namespaceDeclaration = null;
if(namespacePrefix != null) {
namespaceDeclaration = contextElement.getAttributeNode("xmlns:" + namespacePrefix);
} else {
//look for default namespace declaration for null prefix
namespaceDeclaration = contextElement.getAttributeNode(javax.xml.XMLConstants.XMLNS_ATTRIBUTE);
}
if (null != namespaceDeclaration) {
return namespaceDeclaration.getValue();
}
}
Node parentNode = contextNode.getParentNode();
if (parentNode!=null && parentNode.getNodeType() == Node.ELEMENT_NODE) {
return resolveNamespacePrefix(parentNode, namespacePrefix);
}
return null;
}
@Override
public boolean validateDocument(Document document, URL xmlSchemaURL, ErrorHandler errorHandler) throws XMLPlatformException {
Schema xmlSchema;
try {
xmlSchema = getSchemaFactory().newSchema(xmlSchemaURL);
} catch(SAXException e) {
throw XMLPlatformException.xmlPlatformErrorResolvingXMLSchema(xmlSchemaURL, e);
}
try {
Validator validator = xmlSchema.newValidator();
validator.setErrorHandler(errorHandler);
validator.validate(new DOMSource(document));
} catch(Exception e) {
throw XMLPlatformException.xmlPlatformValidationException(e);
}
return true;
}
@Override
public boolean validate(Element elem, org.eclipse.persistence.oxm.XMLDescriptor xmlDescriptor, ErrorHandler handler) throws XMLPlatformException {
return true;
}
@Override
public void namespaceQualifyFragment(Element next) {
namespaceQualifyFragment(next, new ArrayList<String>());
}
//pass list of prefixes declared and encountered
private void namespaceQualifyFragment(Element next, List<String> declaredPrefixes) {
String elementUri = next.getNamespaceURI();
String elementPrefix = next.getPrefix();
if (elementPrefix != null) {
//see if this prefix is already declared if yes - do nothing, if no declare
Attr namespaceDeclaration = next.getAttributeNode(javax.xml.XMLConstants.XMLNS_ATTRIBUTE+":" + elementPrefix);
if ((null == namespaceDeclaration) && !declaredPrefixes.contains(elementPrefix)) {
(next).setAttributeNS(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, javax.xml.XMLConstants.XMLNS_ATTRIBUTE+ ":" + elementPrefix, elementUri);
declaredPrefixes.add(elementPrefix);
}
}
//check all attributes prefixes and if any of them arent declared add them also.
NamedNodeMap attributes = next.getAttributes();
int attributesSize = attributes.getLength();
for (int i = 0; i < attributesSize; i++) {
Attr nextAttribute = (Attr)attributes.item(i);
String attributePrefix = nextAttribute.getPrefix();
if (attributePrefix != null) {
//if attribute is a namespace declaration add to declared list
if (javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(nextAttribute.getNamespaceURI())) {
declaredPrefixes.add(nextAttribute.getLocalName());
} else {
Attr namespaceDeclaration = next.getAttributeNode(javax.xml.XMLConstants.XMLNS_ATTRIBUTE +":" + attributePrefix);
if ((null == namespaceDeclaration) && !declaredPrefixes.contains(attributePrefix)) {
String attributeUri = nextAttribute.getNamespaceURI();
(next).setAttributeNS(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, javax.xml.XMLConstants.XMLNS_ATTRIBUTE + ":" + attributePrefix, attributeUri);
declaredPrefixes.add(attributePrefix);
}
//if xsi:type declaration deal with that value
if (javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI.equals(nextAttribute.getNamespaceURI()) && Constants.SCHEMA_TYPE_ATTRIBUTE.equals(nextAttribute.getLocalName())) {
String value = nextAttribute.getValue();
int colonIndex = value.indexOf(':');
if (colonIndex > -1) {
String prefix = value.substring(0, colonIndex);
namespaceDeclaration = next.getAttributeNode(javax.xml.XMLConstants.XMLNS_ATTRIBUTE +":" + prefix);
if ((null == namespaceDeclaration) && !declaredPrefixes.contains(prefix)) {
String uri = XMLPlatformFactory.getInstance().getXMLPlatform().resolveNamespacePrefix(next, prefix);
(next).setAttributeNS(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, javax.xml.XMLConstants.XMLNS_ATTRIBUTE + ":" + prefix, uri);
declaredPrefixes.add(prefix);
}
}
}
}
}
}
NodeList children = next.getChildNodes();
int numberOfNodes = children.getLength();
for (int i = 0; i < numberOfNodes; i++) {
Node nextNode = children.item(i);
if (nextNode.getNodeType() == Node.ELEMENT_NODE) {
Element child = (Element)nextNode;
namespaceQualifyFragment(child, declaredPrefixes);
}
}
}
@Override
public void setDisableSecureProcessing(boolean disableSecureProcessing) {
boolean shouldReset = this.disableSecureProcessing ^ disableSecureProcessing;
this.disableSecureProcessing = disableSecureProcessing;
if (shouldReset) {
documentBuilderFactory = null;
schemaFactory = null;
xPathFactory = null;
}
}
@Override
public boolean isSecureProcessingDisabled() {
return disableSecureProcessing;
}
}