/* * © Copyright IBM Corp. 2012 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.ibm.commons.xml.drivers; import java.io.IOException; import java.io.OutputStream; import java.io.Writer; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.TransformerException; import org.apache.xerces.dom.NodeImpl; import org.apache.xerces.jaxp.DocumentBuilderFactoryImpl; import org.apache.xml.serialize.OutputFormat; import org.apache.xml.serialize.XMLSerializer; import org.apache.xml.utils.PrefixResolver; import org.apache.xpath.XPathAPI; import org.apache.xpath.objects.XObject; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import org.w3c.dom.DocumentFragment; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.ibm.commons.util.StringUtil; import com.ibm.commons.xml.Format; import com.ibm.commons.xml.NamespaceContext; import com.ibm.commons.xml.XMLException; import com.ibm.commons.xml.XPathContext; import com.ibm.commons.xml.XResult; import com.ibm.commons.xml.XResultUtils; import com.ibm.commons.xml.xpath.NodeListImpl; import com.ibm.commons.xml.xpath.XPathException; /** * Basic XERCES services. * This class encapsulate the basic document creation as well as the XPath service. */ public abstract class AbstractXercesDriver extends AbstractDriver { private DOMImplementation domImplementation; public AbstractXercesDriver() { this.domImplementation = org.apache.xerces.dom.DOMImplementationImpl.getDOMImplementation(); } public DOMImplementation getDOMImplementation() { return domImplementation; } protected DocumentBuilderFactory createDocumentBuilderFactory(boolean resolveEntities, boolean validate) { DocumentBuilderFactory dbFactory = new DocumentBuilderFactoryImpl(); dbFactory.setNamespaceAware(true); dbFactory.setExpandEntityReferences(resolveEntities); dbFactory.setValidating(validate); return dbFactory; } // ========================================================================================= // NamespaceContext Access // ========================================================================================= private static class UserData { private NamespaceContext nsContext; private XPathContext xpContext; } private UserData getUserData(Document doc) { NodeImpl impl = (NodeImpl)doc; UserData data = (UserData)impl.getUserData(); if(data==null) { data = new UserData(); impl.setUserData(data); } return data; } public XPathContext getXPathContext(Document doc) { UserData data = getUserData(doc); return data.xpContext; } public void pushXPathContext(Document doc, String xpath) throws XMLException { UserData data = getUserData(doc); data.xpContext = new XPathContext(doc,data.xpContext,xpath); } public void popXPathContext(Document doc) throws XMLException { UserData data = getUserData(doc); data.xpContext = data.xpContext.getParent(); } public NamespaceContext getNamespaceContext(Document doc) { UserData data = getUserData(doc); return data.nsContext; } public void setNamespaceContext(Document doc, NamespaceContext ns) { UserData data = getUserData(doc); data.nsContext = ns; } public void serialize(OutputStream os, Node node, Format format) throws XMLException { try { XMLSerializer ser = createXMLSerializer(node,format); ser.setOutputByteStream(os); serialize(ser,node); } catch(Exception e) { throw new XMLException(e,"Error while converting XML document to string"); // $NLS-AbstractXercesDriver.ErrorwhileconvertingXMLdocumentto-1$ } } public void serialize(Writer w, Node node, Format format) throws XMLException { try { XMLSerializer ser = createXMLSerializer(node,format); ser.setOutputCharStream(w); serialize(ser,node); } catch(Exception e) { throw new XMLException(e,"Error while converting XML document to string"); // $NLS-AbstractXercesDriver.ErrorwhileconvertingXMLdocumentto.1-1$ } } private XMLSerializer createXMLSerializer(Node node, Format fmt) { if(fmt==null) { fmt = Format.defaultFormat; } OutputFormat format = new OutputFormat(); //node.getOwnerDocument()); format.setIndent(fmt.indent); format.setOmitXMLDeclaration(!fmt.xmlDecl); format.setEncoding(fmt.encoding); return new XMLSerializer(format); } private void serialize(XMLSerializer ser, Node node) throws IOException { if(node instanceof Document) { ser.serialize((Document)node); } else if(node instanceof Element) { ser.serialize((Element)node); } else if(node instanceof DocumentFragment) { ser.serialize((DocumentFragment)node); } } // ========================================================================================= // XPATH // ========================================================================================= // PHIL: temporary. // Not optimized method. Should move to a precompiled XPath object public Object createXPath(String xpath) throws XPathException { return xpath; } public XResult evaluateXPath(Node node, Object xpath, NamespaceContext nsContext) throws XPathException { try { PrefixResolver pr = nsContext!=null ? new NSResolver(nsContext) : null; XObject res = XPathAPI.eval(node,(String)xpath,pr); return convertResult(res); } catch(Exception e) { throw new XPathException(e,"Error while evaluating XPath {0}",xpath.toString()); // $NLS-AbstractXercesDriver.ErrorwhileevaluatingXPath0-1$ } } public XResult evaluateXPath(NodeList nodeList, Object xpath, NamespaceContext nsContext) throws XPathException { try { if(nodeList.getLength()>0) { XResult result = null; NodeListImpl nodes = null; PrefixResolver pr = nsContext!=null ? new NSResolver(nsContext) : null; for( int i=0; i<nodeList.getLength(); i++ ) { XObject res = XPathAPI.eval(nodeList.item(i),(String)xpath,pr); switch(res.getType()) { case XObject.CLASS_BOOLEAN: { if(nodes!=null) { throw new XPathException(null,"XPath result cannot contain both values and nodes"); // $NLS-AbstractXercesDriver.AnXPathresultcannotcontainbothval-1$ } if(result!=null) { throw new XPathException(null,"XPath exception cannot return multiple values"); // $NLS-AbstractXercesDriver.AnXPathExceptioncannotreturnmulti-1$ } result = new XResultUtils.BooleanValue(res.bool()); } break; case XObject.CLASS_NUMBER: { if(nodes!=null) { throw new XPathException(null,"XPath result cannot contain both values and nodes"); // $NLS-AbstractXercesDriver.AnXPathresultcannotcontainbothval.1-1$ } if(result!=null) { throw new XPathException(null,"XPath exception cannot return multiple values"); // $NLS-AbstractXercesDriver.AnXPathExceptioncannotreturnmulti.1-1$ } result = new XResultUtils.NumberValue(res.num()); } break; case XObject.CLASS_STRING: { if(nodes!=null) { throw new XPathException(null,"XPath result cannot contain both values and nodes"); // $NLS-AbstractXercesDriver.AnXPathresultcannotcontainbothval.2-1$ } if(result!=null) { throw new XPathException(null,"XPath exception cannot return multiple values"); // $NLS-AbstractXercesDriver.AnXPathExceptioncannotreturnmulti.2-1$ } result = new XResultUtils.StringValue(res.str()); } break; case XObject.CLASS_NODESET: { if(result!=null) { throw new XPathException(null,"XPath result cannot contain both values and nodes"); // $NLS-AbstractXercesDriver.AnXPathresultcannotcontainbothval.3-1$ } NodeList nl = res.nodelist(); int len = nl.getLength(); if(len>0) { if(nodes==null) { nodes = new NodeListImpl(); } for( int j=0; j<nl.getLength(); j++ ) { nodes.add(nl.item(j)); } } } } } if(result!=null) { return result; } if(nodes!=null) { int len = nodes.getLength(); if(len==0) { return XResultUtils.emptyResult; } else if(len==1) { return new XResultUtils.XMLNode(nodes.item(0)); } else { return new XResultUtils.XMLNodeList(nodes); } } } return XResultUtils.emptyResult; } catch(Exception e) { throw new XPathException(e,"Error evaluating XPath {0}",xpath.toString()); // $NLS-AbstractXercesDriver.ErrorwhileevaluatingXPath0.1-1$ } } private static class NSResolver implements PrefixResolver { NamespaceContext nsContext; NSResolver(NamespaceContext nsContext) { this.nsContext = nsContext; } public String getNamespaceForPrefix(String prefix) { return nsContext.getNamespaceURI(prefix); } public String getNamespaceForPrefix(String prefix, org.w3c.dom.Node context) { return nsContext.getNamespaceURI(prefix); } public boolean handlesNullPrefixes() { String nsUri = nsContext.getNamespaceURI(""); return !StringUtil.isEmpty(nsUri); } // PHIL: not sure what to do here... public String getBaseIdentifier() { return ""; } } private static XResult convertResult(XObject object) throws TransformerException { switch(object.getType()) { //case XObject.CLASS_UNKNOWN: //case XObject.CLASS_NULL: // break; case XObject.CLASS_BOOLEAN: { return new XResultUtils.BooleanValue(object.bool()); } case XObject.CLASS_NUMBER: { return new XResultUtils.NumberValue(object.num()); } case XObject.CLASS_STRING: { return new XResultUtils.StringValue(object.str()); } case XObject.CLASS_NODESET: { final NodeList nl = object.nodelist(); final int len = nl.getLength(); if(len==0) { return XResultUtils.emptyResult; } else if(len==1) { return new XResultUtils.XMLNode(nl.item(0)); } else { return new XResultUtils.XMLNodeList(nl); } } //case CLASS_RTREEFRAG: { //CLASS_UNRESOLVEDVARIABLE = 600; } return XResultUtils.emptyResult; } }