/******************************************************************************* | |
* Copyright (c) 1998, 2013 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 v1.0 and Eclipse Distribution License v. 1.0 | |
* which accompanies this distribution. | |
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html | |
* and the Eclipse Distribution License is available at | |
* http://www.eclipse.org/org/documents/edl-v10.php. | |
* | |
* 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.List; | |
import java.util.Map; | |
import java.util.ArrayList; | |
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 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; | |
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; | |
/** | |
* <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. | |
* @param contextNode | |
* @param xPath | |
* @param xmlNamespaceResolver | |
* @return | |
* @throws XMLPlatformException | |
*/ | |
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 | |
* @throws XMLPlatformException | |
*/ | |
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); | |
} | |
} | |
public Document createDocument() throws XMLPlatformException { | |
return new XMLDocument(); | |
} | |
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); | |
} | |
} | |
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); | |
} | |
} | |
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; | |
} | |
} | |
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; | |
} | |
public XMLParser newXMLParser() { | |
return new XDKParser(); | |
} | |
public XMLParser newXMLParser(Map<String, Boolean> parserFeatures) { | |
return new XDKParser(); | |
} | |
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 | |
*/ | |
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 document - the document which contains the document fragment to be validated | |
* @param schemaReference - the path to the complex type or element to be validated against in the schema | |
* @param handler - the error handler | |
* @return true if the document fragment is valid, false otherwise | |
*/ | |
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; | |
} | |
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); | |
} | |
} | |
} | |
} |