/******************************************************************************* *******************************************************************************/ package at.gv.egiz.eaaf.core.impl.utils; import java.util.List; import java.util.Map; import org.jaxen.JaxenException; import org.jaxen.NamespaceContext; import org.jaxen.Navigator; import org.jaxen.SimpleNamespaceContext; import org.jaxen.dom.DOMXPath; import org.jaxen.dom.DocumentNavigator; import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.traversal.NodeIterator; import at.gv.egiz.eaaf.core.api.data.XMLNamespaceConstants; import at.gv.egiz.eaaf.core.exceptions.XPathException; /** * Utility methods to evaluate XPath expressions on DOM nodes. * * @author Patrick Peck * @version $Id$ */ public class XPathUtils { /** * The XPath expression selecting all nodes under a given root (including the * root node itself). */ public static final String ALL_NODES_XPATH = "(.//. | .//@* | .//namespace::*)"; /** The DocumentNavigator to use for navigating the document. */ private static Navigator documentNavigator = DocumentNavigator.getInstance(); /** The default namespace prefix to namespace URI mappings. */ private static NamespaceContext NS_CONTEXT; static { SimpleNamespaceContext ctx = new SimpleNamespaceContext(); ctx.addNamespace(XMLNamespaceConstants.MOA_PREFIX, XMLNamespaceConstants.MOA_NS_URI); ctx.addNamespace(XMLNamespaceConstants.MOA_CONFIG_PREFIX, XMLNamespaceConstants.MOA_CONFIG_NS_URI); ctx.addNamespace(XMLNamespaceConstants.MOA_ID_CONFIG_PREFIX, XMLNamespaceConstants.MOA_ID_CONFIG_NS_URI); ctx.addNamespace(XMLNamespaceConstants.SL10_PREFIX, XMLNamespaceConstants.SL10_NS_URI); ctx.addNamespace(XMLNamespaceConstants.SL11_PREFIX, XMLNamespaceConstants.SL11_NS_URI); ctx.addNamespace(XMLNamespaceConstants.SL12_PREFIX, XMLNamespaceConstants.SL12_NS_URI); ctx.addNamespace(XMLNamespaceConstants.ECDSA_PREFIX, XMLNamespaceConstants.ECDSA_NS_URI); ctx.addNamespace(XMLNamespaceConstants.PD_PREFIX, XMLNamespaceConstants.PD_NS_URI); ctx.addNamespace(XMLNamespaceConstants.SAML_PREFIX, XMLNamespaceConstants.SAML_NS_URI); ctx.addNamespace(XMLNamespaceConstants.SAMLP_PREFIX, XMLNamespaceConstants.SAMLP_NS_URI); ctx.addNamespace(XMLNamespaceConstants.DSIG_PREFIX, XMLNamespaceConstants.DSIG_NS_URI); ctx.addNamespace(XMLNamespaceConstants.XSLT_PREFIX, XMLNamespaceConstants.XSLT_NS_URI); ctx.addNamespace(XMLNamespaceConstants.XSI_PREFIX, XMLNamespaceConstants.XSI_NS_URI); ctx.addNamespace(XMLNamespaceConstants.DSIG_FILTER2_PREFIX, XMLNamespaceConstants.DSIG_FILTER2_NS_URI); ctx.addNamespace(XMLNamespaceConstants.DSIG_EC_PREFIX, XMLNamespaceConstants.DSIG_EC_NS_URI); ctx.addNamespace(XMLNamespaceConstants.MD_PREFIX, XMLNamespaceConstants.MD_NS_URI); ctx.addNamespace(XMLNamespaceConstants.MDP_PREFIX, XMLNamespaceConstants.MDP_NS_URI); ctx.addNamespace(XMLNamespaceConstants.MVV_PREFIX, XMLNamespaceConstants.MVV_NS_URI); ctx.addNamespace(XMLNamespaceConstants.STB_PREFIX, XMLNamespaceConstants.STB_NS_URI); ctx.addNamespace(XMLNamespaceConstants.WRR_PREFIX, XMLNamespaceConstants.WRR_NS_URI); ctx.addNamespace(XMLNamespaceConstants.STORK_PREFIX, XMLNamespaceConstants.STORK_NS_URI); ctx.addNamespace(XMLNamespaceConstants.STORKP_PREFIX, XMLNamespaceConstants.STORKP_NS_URI); ctx.addNamespace(XMLNamespaceConstants.SAML2_PREFIX, XMLNamespaceConstants.SAML2_NS_URI); ctx.addNamespace(XMLNamespaceConstants.SAML2P_PREFIX, XMLNamespaceConstants.SAML2P_NS_URI); ctx.addNamespace(XMLNamespaceConstants.XENC_PREFIX, XMLNamespaceConstants.XENC_NS_URI); ctx.addNamespace(XMLNamespaceConstants.XADES_1_1_1_NS_PREFIX, XMLNamespaceConstants.XADES_1_1_1_NS_URI); NS_CONTEXT = ctx; } /** * Return a NodeIterator over the nodes matching the XPath * expression. * * All namespace URIs and prefixes declared in the Constants * interface are used for resolving namespaces. * * @param contextNode The root node from which to evaluate the XPath * expression. * @param exp The XPath expression to evaluate. * @return An iterator over the resulting nodes. * @throws XPathException An error occurred evaluating the XPath expression. */ public static NodeIterator selectNodeIterator(Node contextNode, String exp) throws XPathException { return selectNodeIterator(contextNode, NS_CONTEXT, exp); } /** * Return a NodeIterator over the nodes matching the XPath * expression. * * @param contextNode The root node from which to evaluate the XPath * expression. * @param namespaceElement An element from which to build the * namespace mapping for evaluating the XPath expression * @param exp The XPath expression to evaluate. * @return An iterator over the resulting nodes. * @throws XPathException An error occurred evaluating the XPath expression. */ public static NodeIterator selectNodeIterator( Node contextNode, Element namespaceElement, String exp) throws XPathException { try { SimpleNamespaceContext ctx = new SimpleNamespaceContext(); ctx.addElementNamespaces(documentNavigator, namespaceElement); return selectNodeIterator(contextNode, ctx, exp); } catch (JaxenException e) { throw new XPathException("XPath operation FAILED. Reason: " + e.getMessage(), e); } } /** * Return a NodeIterator over the nodes matching the XPath * expression. * * @param contextNode The root node from which to evaluate the XPath * expression. * @param namespaceMapping A namespace prefix to namespace URI mapping * (String to String) for evaluating the XPath * expression. * @param exp The XPath expression to evaluate. * @return An iterator over the resulting nodes. * @throws XPathException An error occurred evaluating the XPath expression. */ public static NodeIterator selectNodeIterator( Node contextNode, Map namespaceMapping, String exp) throws XPathException { SimpleNamespaceContext ctx = new SimpleNamespaceContext(namespaceMapping); return selectNodeIterator(contextNode, ctx, exp); } /** * Return a NodeIterator over the nodes matching the XPath * expression. * * @param contextNode The root node from which to evaluate the XPath * expression. * @param nsContext The NamespaceContext for resolving namespace * prefixes to namespace URIs for evaluating the XPath expression. * @param exp The XPath expression to evaluate. * @return An iterator over the resulting nodes. * @throws XPathException An error occurred evaluating the XPath expression. */ private static NodeIterator selectNodeIterator( Node contextNode, NamespaceContext nsContext, String exp) throws XPathException { try { DOMXPath xpath = new DOMXPath(exp); List nodes; xpath.setNamespaceContext(nsContext); nodes = xpath.selectNodes(contextNode); return new NodeIteratorAdapter(nodes.listIterator()); } catch (JaxenException e) { throw new XPathException("XPath operation FAILED. Reason: " + e.getMessage(), e); } } /** * Return a NodeList of all the nodes matching the XPath * expression. * * All namespace URIs and prefixes declared in the Constants * interface are used for resolving namespaces. * * @param contextNode The root node from which to evaluate the XPath * expression. * @param exp The XPath expression to evaluate. * @return A NodeList containing the matching nodes. * @throws XPathException An error occurred evaluating the XPath expression. */ public static NodeList selectNodeList(Node contextNode, String exp) throws XPathException { return selectNodeList(contextNode, NS_CONTEXT, exp); } /** * Return a NodeList of all the nodes matching the XPath * expression. * * @param contextNode The root node from which to evaluate the XPath * expression. * @param namespaceElement An element from which to build the * namespace mapping for evaluating the XPath expression * @param exp The XPath expression to evaluate. * @return A NodeList containing the matching nodes. * @throws XPathException An error occurred evaluating the XPath expression. */ public static NodeList selectNodeList( Node contextNode, Element namespaceElement, String exp) throws XPathException { try { SimpleNamespaceContext ctx = new SimpleNamespaceContext(); ctx.addElementNamespaces(documentNavigator, namespaceElement); return selectNodeList(contextNode, ctx, exp); } catch (JaxenException e) { throw new XPathException("XPath operation FAILED. Reason: " + e.getMessage(), e); } } /** * Return a NodeList of all the nodes matching the XPath * expression. * * @param contextNode The root node from which to evaluate the XPath * expression. * @param namespaceMapping A namespace prefix to namespace URI mapping * (String to String) for evaluating the XPath * expression. * @param exp The XPath expression to evaluate. * @return A NodeList containing the matching nodes. * @throws XPathException An error occurred evaluating the XPath expression. */ public static NodeList selectNodeList( Node contextNode, Map namespaceMapping, String exp) throws XPathException { SimpleNamespaceContext ctx = new SimpleNamespaceContext(namespaceMapping); return selectNodeList(contextNode, ctx, exp); } /** * Return a NodeList of all the nodes matching the XPath * expression. * * @param contextNode The root node from which to evaluate the XPath * expression. * @param nsContext The NamespaceContext for resolving namespace * prefixes to namespace URIs for evaluating the XPath expression. * @param exp The XPath expression to evaluate. * @return A NodeList containing the matching nodes. * @throws XPathException An error occurred evaluating the XPath expression. */ private static NodeList selectNodeList(Node contextNode, NamespaceContext nsContext, String exp) throws XPathException { try { DOMXPath xpath = new DOMXPath(exp); List nodes; xpath.setNamespaceContext(nsContext); nodes = xpath.selectNodes(contextNode); return new NodeListAdapter(nodes); } catch (JaxenException e) { throw new XPathException("XPath operation FAILED. Reason: " + e.getMessage(), e); } } /** * Select the first node matching an XPath expression. * * All namespace URIs and prefixes declared in the Constants * interface are used for resolving namespaces. * * @param contextNode The root node from which to evaluate the XPath * expression. * @param exp The XPath expression to evaluate. * @return Node The first node matching the XPath expression, or * null, if no node matched. * @throws XPathException An error occurred evaluating the XPath expression. */ public static Node selectSingleNode(Node contextNode, String exp) throws XPathException { return selectSingleNode(contextNode, NS_CONTEXT, exp); } /** * Select the first node matching an XPath expression. * * @param contextNode The root node from which to evaluate the XPath * expression. * @param namespaceElement An element from which to build the * namespace mapping for evaluating the XPath expression * @param exp The XPath expression to evaluate. * @return Node The first node matching the XPath expression, or * null, if no node matched. * @throws XPathException An error occurred evaluating the XPath expression. */ public static Node selectSingleNode( Node contextNode, Element namespaceElement, String exp) throws XPathException { try { SimpleNamespaceContext ctx = new SimpleNamespaceContext(); ctx.addElementNamespaces(documentNavigator, namespaceElement); return selectSingleNode(contextNode, ctx, exp); } catch (JaxenException e) { throw new XPathException("XPath operation FAILED. Reason: " + e.getMessage(), e); } } /** * Select the first node matching an XPath expression. * * @param contextNode The root node from which to evaluate the XPath * expression. * @param namespaceMapping A namespace prefix to namespace URI mapping * (String to String) for evaluating the XPath * expression. * @param exp The XPath expression to evaluate. * @return Node The first node matching the XPath expression, or * null, if no node matched. * @throws XPathException An error occurred evaluating the XPath expression. */ public static Node selectSingleNode( Node contextNode, Map namespaceMapping, String exp) throws XPathException { SimpleNamespaceContext ctx = new SimpleNamespaceContext(namespaceMapping); return selectSingleNode(contextNode, ctx, exp); } /** * Select the first node matching an XPath expression. * * @param contextNode The root node from which to evaluate the XPath * expression. * @param nsContext The NamespaceContext for resolving namespace * prefixes to namespace URIs for evaluating the XPath expression. * @param exp The XPath expression to evaluate. * @return Node The first node matching the XPath expression, or * null, if no node matched. * @throws XPathException An error occurred evaluating the XPath expression. */ public static Node selectSingleNode( Node contextNode, NamespaceContext nsContext, String exp) throws XPathException { try { DOMXPath xpath = new DOMXPath(exp); xpath.setNamespaceContext(nsContext); return (Node) xpath.selectSingleNode(contextNode); } catch (JaxenException e) { throw new XPathException("XPath operation FAILED. Reason: " + e.getMessage(), e); } } /** * Return the value of a DOM element whose location is given by an XPath * expression. * * @param root The root element from which to evaluate the XPath. * @param xpath The XPath expression pointing to the element whose value * to return. * @param def The default value to return, if no element can be found using * the given xpath. * @return The element value, if it can be located using the * xpath. Otherwise, def is returned. */ public static String getElementValue( Element root, String xpath, String def) { Element elem = (Element) XPathUtils.selectSingleNode(root, xpath); return elem != null ? DOMUtils.getText(elem) : def; } /** * Return the value of a DOM attribute whose location is given by an XPath * expression. * * @param root The root element from which to evaluate the XPath. * @param xpath The XPath expression pointing to the attribute whose value to * return. * @param def The default value to return, if no attribute can be found using * the given xpath. * @return The element value, if it can be located using the * xpath. Otherwise, def is returned. */ public static String getAttributeValue( Element root, String xpath, String def) { Attr attr = (Attr) XPathUtils.selectSingleNode(root, xpath); return attr != null ? attr.getValue() : def; } /** * Returns the namespace prefix used within XPathUtils for referring to * the namespace of the specified (Security Layer command) element. * * This namespace prefix can be used in various XPath expression evaluation methods * within XPathUtils without explicitely binding it to the particular * namespace. * * @param contextElement The (Security Layer command) element. * * @return the namespace prefix used within XPathUtils for referring to * the namespace of the specified (Security Layer command) element. * * throws XpathException If the specified element has a namespace other than the ones * known by this implementation as valid Security Layer namespaces (cf. * @link Constants#SL10_NS_URI, @link Constants#SL11_NS_URI, @link Constants#SL12_NS_URI). */ public static String getSlPrefix (Element contextElement) throws XPathException { String sLNamespace = contextElement.getNamespaceURI(); String sLPrefix = null; if (sLNamespace.equals(XMLNamespaceConstants.SL10_NS_URI)) sLPrefix = XMLNamespaceConstants.SL10_PREFIX; else if (sLNamespace.equals(XMLNamespaceConstants.SL12_NS_URI)) sLPrefix = XMLNamespaceConstants.SL12_PREFIX; else if (sLNamespace.equals(XMLNamespaceConstants.SL11_NS_URI)) sLPrefix = XMLNamespaceConstants.SL11_PREFIX; else throw new XPathException("XPath operation FAILED. Reason: "); return sLPrefix; } /** * Return the SecurityLayer namespace prefix of the context element. * If the context element is not the element that lies within the * SecurityLayer namespace. The Securitylayer namespace is derived from * the xmlns:sl10, sl11 or sl * attribute of the context element. * * The returned prefix is needed for evaluating XPATH expressions. * * @param contextElement The element to get a prefix for the Securitylayer namespace, * that is used within the corresponding document. * * @return The string sl10, sl11 or sl, * depending on the SecurityLayer namespace of the contextElement. * * throws XPathException If no (vlalid) SecurityLayer namespace prefix or namespace * is defined. */ public static String getSlPrefixFromNoRoot (Element contextElement) throws XPathException { String slPrefix = checkSLnsDeclaration(contextElement, XMLNamespaceConstants.SL10_PREFIX, XMLNamespaceConstants.SL10_NS_URI); if (slPrefix == null) slPrefix = checkSLnsDeclaration(contextElement, XMLNamespaceConstants.SL11_PREFIX, XMLNamespaceConstants.SL11_NS_URI); if (slPrefix == null) slPrefix = checkSLnsDeclaration(contextElement, XMLNamespaceConstants.SL12_PREFIX, XMLNamespaceConstants.SL12_NS_URI); return slPrefix; } /** * Checks if the context element has an attribute xmlns:slPrefix and * if the prefix of that attribute corresponds with a valid SecurityLayer namespace. * * @param contextElement The element to be checked. * @param slPrefix The prefix which should be checked. Must be a valid SecurityLayer * namespace prefix. * @param slNameSpace The SecurityLayer namespace that corresponds to the specified prefix. * * @return The valid SecurityLayer prefix or null if this prefix is * not used. * @throws XPathException */ private static String checkSLnsDeclaration(Element contextElement, String slPrefix, String slNameSpace) throws XPathException { String nsAtt = "xmlns:" + slPrefix; String nameSpace = contextElement.getAttribute(nsAtt); if (nameSpace == "") { return null; } else { // check if namespace is correct if (nameSpace.equals(slNameSpace)) return slPrefix; else throw new XPathException("Unknown Namespace declaration"); } } }