| /* |
| * Copyright (c) 1998, 2020 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 |
| * @throws XMLPlatformException |
| */ |
| @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. |
| * @param contextNode |
| * @param xPathString |
| * @param xmlNamespaceResolver |
| * @return |
| * @throws XMLPlatformException |
| */ |
| @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; |
| } |
| |
| } |