| /* |
| * 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.xdk; |
| |
| import java.lang.reflect.Method; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.StringTokenizer; |
| |
| import org.eclipse.persistence.internal.helper.Helper; |
| import org.eclipse.persistence.oxm.NamespaceResolver; |
| import org.eclipse.persistence.oxm.XMLConstants; |
| import org.eclipse.persistence.oxm.XMLDescriptor; |
| 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.XMLSchemaReference; |
| import org.eclipse.persistence.platform.xml.XMLTransformer; |
| import org.w3c.dom.Attr; |
| 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.ErrorHandler; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.SAXParseException; |
| |
| import oracle.xml.parser.schema.XMLSchema; |
| import oracle.xml.parser.schema.XSDBuilder; |
| import oracle.xml.parser.schema.XSDComplexType; |
| import oracle.xml.parser.schema.XSDConstantValues; |
| import oracle.xml.parser.schema.XSDElement; |
| import oracle.xml.parser.schema.XSDNode; |
| import oracle.xml.parser.schema.XSDValidator; |
| import oracle.xml.parser.v2.XMLDocument; |
| import oracle.xml.parser.v2.XMLElement; |
| import oracle.xml.parser.v2.XMLError; |
| import oracle.xml.parser.v2.XMLNode; |
| import oracle.xml.parser.v2.XMLParseException; |
| import oracle.xml.parser.v2.XSLException; |
| |
| /** |
| * <p><b>Purpose</b>: An implementation of XMLPlatform using Oracle XDK APIs.</p> |
| */ |
| |
| public class XDKPlatform implements XMLPlatform { |
| private Method buildSchemaMethod; |
| |
| public XDKPlatform() { |
| super(); |
| try { |
| Class<?>[] argTypes = { URL.class }; |
| buildSchemaMethod = Helper.getDeclaredMethod(XSDBuilder.class, "build", argTypes); |
| } catch (NoSuchMethodException e) { |
| } |
| } |
| |
| /** |
| * Execute advanced XPath statements that are required for TopLink EIS. |
| */ |
| @Override |
| public Node selectSingleNodeAdvanced(Node contextNode, String xPath, XMLNamespaceResolver xmlNamespaceResolver) throws XMLPlatformException { |
| try { |
| XMLNode xmlNode = (XMLNode)contextNode; |
| XDKNamespaceResolver xdkNamespaceResolver = new XDKNamespaceResolver(xmlNamespaceResolver); |
| return xmlNode.selectSingleNode(xPath, xdkNamespaceResolver); |
| } catch (XSLException e) { |
| throw XMLPlatformException.xmlPlatformInvalidXPath(e); |
| } |
| } |
| |
| /** |
| * 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 xPath, XMLNamespaceResolver xmlNamespaceResolver) throws XMLPlatformException { |
| try { |
| XMLNode xmlNode = (XMLNode)contextNode; |
| XDKNamespaceResolver xdkNamespaceResolver = new XDKNamespaceResolver(xmlNamespaceResolver); |
| return xmlNode.selectNodes(xPath, xdkNamespaceResolver); |
| } catch (XSLException e) { |
| throw XMLPlatformException.xmlPlatformInvalidXPath(e); |
| } |
| } |
| |
| @Override |
| public Document createDocument() throws XMLPlatformException { |
| return new XMLDocument(); |
| } |
| |
| @Override |
| public Document createDocumentWithPublicIdentifier(String name, String publicIdentifier, String systemIdentifier) throws XMLPlatformException { |
| try { |
| XMLDocument xmlDocument = (XMLDocument)createDocument(); |
| Element rootElement = xmlDocument.createElement(name); |
| xmlDocument.appendChild(rootElement); |
| xmlDocument.setDoctype(name, systemIdentifier, publicIdentifier); |
| return xmlDocument; |
| } catch (Exception e) { |
| throw XMLPlatformException.xmlPlatformCouldNotCreateDocument(e); |
| } |
| } |
| |
| @Override |
| public Document createDocumentWithSystemIdentifier(String name, String systemIdentifier) throws XMLPlatformException { |
| try { |
| if (null == systemIdentifier) { |
| Document document = createDocument(); |
| Element rootElement = document.createElement(name); |
| document.appendChild(rootElement); |
| return document; |
| } |
| |
| XMLDocument xmlDocument = (XMLDocument)createDocument(); |
| Element rootElement = xmlDocument.createElement(name); |
| xmlDocument.appendChild(rootElement); |
| xmlDocument.setDoctype(name, systemIdentifier, null); |
| return xmlDocument; |
| } catch (Exception e) { |
| throw XMLPlatformException.xmlPlatformCouldNotCreateDocument(e); |
| } |
| } |
| |
| @Override |
| public boolean isWhitespaceNode(Text text) { |
| try { |
| String value = text.getNodeValue(); |
| if (null == value) { |
| return false; |
| } else { |
| return value.trim().equals(""); |
| } |
| } catch (NullPointerException e) { |
| // The 9.0.4 XDK will throw a NPE on getNoderValue() if the node value is null. |
| return false; |
| } |
| } |
| |
| @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(XMLConstants.XMLNS); |
| } |
| 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 XMLParser newXMLParser() { |
| return new XDKParser(); |
| } |
| |
| @Override |
| public XMLParser newXMLParser(Map<String, Boolean> parserFeatures) { |
| return new XDKParser(); |
| } |
| |
| @Override |
| public XMLTransformer newXMLTransformer() { |
| return new XDKTransformer(); |
| } |
| |
| /** |
| * Validates a document against an XML schema |
| * |
| * @param document - the document to be validated |
| * @param xmlSchemaURL - the schema URL |
| * @param errorHandler - the error handler |
| * @return true if the document fragment is valid, false otherwise |
| */ |
| @Override |
| public boolean validateDocument(Document document, URL xmlSchemaURL, ErrorHandler errorHandler) throws XMLPlatformException { |
| XMLSchema xmlSchema = null; |
| XSDValidator validator = null; |
| try { |
| Object[] args = { xmlSchemaURL }; |
| xmlSchema = (XMLSchema)buildSchemaMethod.invoke(new XSDBuilder(), args); |
| validator = new XSDValidator(); |
| } catch (Exception e) { |
| throw XMLPlatformException.xmlPlatformErrorResolvingXMLSchema(xmlSchemaURL, e); |
| } |
| |
| // set the schema to be validated against |
| validator.setXMLProperty(XSDValidator.FIXED_SCHEMA, xmlSchema); |
| |
| XMLError xmlErr = new XMLError(); |
| try { |
| validator.setError(xmlErr); |
| } catch (org.xml.sax.SAXException saxex) { |
| throw XMLPlatformException.xmlPlatformValidationException(saxex); |
| } |
| |
| try { |
| ((XMLDocument)document).validateContent(validator, true); |
| } catch (XMLParseException e) { |
| // Ignore this exception, the XMLError will be used to determine if theree |
| // were any errors. |
| } |
| handleErrors(xmlErr, errorHandler); |
| |
| return true; |
| } |
| |
| /** |
| * Validates a document fragment against a complex type or element in the XML schema |
| * |
| * @param elem - the document which contains the document fragment to be validated |
| * @param xmlDescriptor - the path to the complex type or element to be validated against in the schema |
| * @param errorHandler - the error handler |
| * @return true if the document fragment is valid, false otherwise |
| */ |
| @Override |
| public boolean validate(Element elem, XMLDescriptor xmlDescriptor, ErrorHandler errorHandler) throws XMLPlatformException { |
| XMLSchemaReference schemaReference = xmlDescriptor.getSchemaReference(); |
| NamespaceResolver nsResolver = xmlDescriptor.getNamespaceResolver(); |
| |
| // build a schema using the URL in the schema reference, and setup a validator |
| XMLSchema xmlSchema; |
| XSDValidator validator = null; |
| try { |
| Object[] args = { schemaReference.getURL() }; |
| xmlSchema = (XMLSchema)buildSchemaMethod.invoke(new XSDBuilder(), args); |
| validator = new XSDValidator(); |
| } catch (Exception ex) { |
| throw XMLPlatformException.xmlPlatformValidationException(ex); |
| } |
| |
| // set the schema to be validated against |
| validator.setXMLProperty(XSDValidator.FIXED_SCHEMA, xmlSchema); |
| |
| // set the node to be validated against |
| XSDNode xsdNode = getNodeFromSchemaReference(xmlSchema, schemaReference, nsResolver); |
| |
| // if xsdNode is null, the schema context string is empty or the target could not be found |
| if (xsdNode == null) { |
| validator.setXMLProperty(XSDNode.ROOT_NODE, null); |
| } |
| |
| if (schemaReference.getType() == XMLSchemaReference.ELEMENT) { |
| if (xmlDescriptor.getDefaultRootElement() != null) { |
| validator.setXMLProperty(XSDNode.ROOT_NODE, xsdNode); |
| } else { |
| validator.setXMLProperty(XSDNode.ROOT_NODE, ((XSDElement)xsdNode).getType()); |
| } |
| } else { |
| validator.setXMLProperty(XSDNode.ROOT_NODE, xsdNode); |
| } |
| |
| XMLError xmlErr = new XMLError(); |
| try { |
| validator.setError(xmlErr); |
| } catch (org.xml.sax.SAXException saxex) { |
| throw XMLPlatformException.xmlPlatformValidationException(saxex); |
| } |
| |
| try { |
| ((XMLElement)elem).validateContent(validator, true); |
| } catch (XMLParseException e) { |
| // Ignore this exception, the XMLError will be used to determine if theree |
| // were any errors. |
| } |
| handleErrors(xmlErr, errorHandler); |
| |
| return true; |
| } |
| |
| private void handleErrors(XMLError xmlErr, ErrorHandler errorHandler) { |
| try { |
| int numberOfMessages = xmlErr.getNumMessages(); |
| SAXParseException saxParseException; |
| for (int x = 0; x < numberOfMessages; x++) { |
| saxParseException = new SAXParseException(xmlErr.getMessage(x), xmlErr.getPublicId(x), xmlErr.getSystemId(x), xmlErr.getLineNumber(x), xmlErr.getColumnNumber(x), xmlErr.getException(x)); |
| |
| if (null == errorHandler) { |
| throw saxParseException; |
| } |
| errorHandler.fatalError(saxParseException); |
| } |
| } catch (SAXException xmlex) { |
| throw XMLPlatformException.xmlPlatformValidationException(xmlex); |
| } |
| } |
| |
| /** |
| * This convenience method will parse a schema reference and return the node to be |
| * validated against. |
| * |
| * @param xmlSchema - the schema to be used for validation |
| * @param schemaRef - the schema reference object |
| * @return the node to be validated against, null if not found |
| */ |
| private XSDNode getNodeFromSchemaReference(XMLSchema xmlSchema, XMLSchemaReference schemaRef, NamespaceResolver nsResolver) { |
| if (schemaRef == null) { |
| return null; |
| } |
| |
| // schema context should be in the format '/prefix:nodeName/.." |
| // tokenize the schema context to find the node that is to be validated |
| StringTokenizer nodes = new StringTokenizer(schemaRef.getSchemaContext(), "/"); |
| |
| // if no tokens, then invalid schema context |
| if (!(nodes.hasMoreTokens())) { |
| return null; |
| } |
| |
| String namespace = ""; |
| String nodeName = nodes.nextToken(); |
| |
| // look for a prefix |
| StringTokenizer prefixes = new StringTokenizer(nodeName, ":"); |
| |
| if (prefixes.countTokens() > 1) { |
| // look for a namespace |
| namespace = nsResolver.resolveNamespacePrefix(prefixes.nextToken()); |
| if (namespace == null) { |
| namespace = ""; |
| } |
| } |
| |
| // else no prefix |
| nodeName = prefixes.nextToken(); |
| |
| // handle simple/complex type definitions |
| if (schemaRef.getType() == XMLSchemaReference.SIMPLE_TYPE) { |
| return xmlSchema.getType(namespace, nodeName, XSDConstantValues.DATATYPE); |
| } |
| |
| if (schemaRef.getType() == XMLSchemaReference.COMPLEX_TYPE) { |
| return xmlSchema.getType(namespace, nodeName, XSDConstantValues.TYPE); |
| } |
| |
| // handle elements |
| XSDNode node = xmlSchema.getElement(namespace, nodeName); |
| |
| // loop through schema context tokens - 'node' will contain the target when completed |
| while (nodes.hasMoreTokens()) { |
| node = findChildNode((XSDElement)node, nodes.nextToken()); |
| |
| // if node is null, couldn't find child |
| if (node == null) { |
| return null; |
| } |
| } |
| return node; |
| } |
| |
| /** |
| * This convenience method will iterate through a parent element's children and return the |
| * node corresponding to 'nodeName'. |
| * |
| * @param parent - the parent element |
| * @param childName - the node name to be located |
| * @return the child node with name matching 'childName', null if not found |
| */ |
| protected XSDNode findChildNode(XSDElement parent, String childName) { |
| XSDNode[] children; |
| XSDNode node = null; |
| boolean successful = false; |
| |
| // get the parent node's children |
| children = ((XSDComplexType)parent.getType()).getElementSet(); |
| |
| // iterate over child nodes looking for the child |
| for (int i = 0; i < children.length; i++) { |
| node = children[i]; |
| |
| if (node.getName().equals(childName)) { |
| successful = true; |
| break; |
| } |
| } |
| |
| if (successful) { |
| return node; |
| } |
| return null; |
| } |
| |
| @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(XMLConstants.XMLNS +":" + elementPrefix); |
| if ((null == namespaceDeclaration) && !declaredPrefixes.contains(elementPrefix)) { |
| (next).setAttributeNS(XMLConstants.XMLNS_URL, XMLConstants.XMLNS + ":" + 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 (XMLConstants.XMLNS_URL.equals(nextAttribute.getNamespaceURI())) { |
| declaredPrefixes.add(nextAttribute.getLocalName()); |
| } else { |
| Attr namespaceDeclaration = next.getAttributeNode(XMLConstants.XMLNS +":" + attributePrefix); |
| if ((null == namespaceDeclaration) && !declaredPrefixes.contains(attributePrefix)) { |
| String attributeUri = nextAttribute.getNamespaceURI(); |
| (next).setAttributeNS(XMLConstants.XMLNS_URL, XMLConstants.XMLNS + ":" + attributePrefix, attributeUri); |
| declaredPrefixes.add(attributePrefix); |
| } |
| |
| //if xsi:type declaration deal with that value |
| if (XMLConstants.SCHEMA_INSTANCE_URL.equals(nextAttribute.getNamespaceURI()) && XMLConstants.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(XMLConstants.XMLNS +":" + prefix); |
| if ((null == namespaceDeclaration) && !declaredPrefixes.contains(prefix)) { |
| String uri = XMLPlatformFactory.getInstance().getXMLPlatform().resolveNamespacePrefix(next, prefix); |
| (next).setAttributeNS(XMLConstants.XMLNS_URL, XMLConstants.XMLNS + ":" + 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) { |
| //no-op |
| } |
| |
| @Override |
| public boolean isSecureProcessingDisabled() { |
| return false; |
| } |
| } |