/* * Copyright 2003 Federal Chancellery Austria * MOA-ID has been developed in a cooperation between BRZ, the Federal * Chancellery Austria - ICT staff unit, and Graz University of Technology. * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works * that you distribute must include a readable copy of the "NOTICE" text file. */ package at.gv.egovernment.moaspss.util; import java.util.List; import java.util.Map; 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 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; /** * 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(Constants.MOA_PREFIX, Constants.MOA_NS_URI); ctx.addNamespace(Constants.MOA_CONFIG_PREFIX, Constants.MOA_CONFIG_NS_URI); ctx.addNamespace(Constants.MOA_ID_CONFIG_PREFIX, Constants.MOA_ID_CONFIG_NS_URI); ctx.addNamespace(Constants.SL10_PREFIX, Constants.SL10_NS_URI); ctx.addNamespace(Constants.SL11_PREFIX, Constants.SL11_NS_URI); ctx.addNamespace(Constants.SL12_PREFIX, Constants.SL12_NS_URI); ctx.addNamespace(Constants.ECDSA_PREFIX, Constants.ECDSA_NS_URI); ctx.addNamespace(Constants.PD_PREFIX, Constants.PD_NS_URI); ctx.addNamespace(Constants.SAML_PREFIX, Constants.SAML_NS_URI); ctx.addNamespace(Constants.SAMLP_PREFIX, Constants.SAMLP_NS_URI); ctx.addNamespace(Constants.DSIG_PREFIX, Constants.DSIG_NS_URI); ctx.addNamespace(Constants.XSLT_PREFIX, Constants.XSLT_NS_URI); ctx.addNamespace(Constants.XSI_PREFIX, Constants.XSI_NS_URI); ctx.addNamespace(Constants.DSIG_FILTER2_PREFIX, Constants.DSIG_FILTER2_NS_URI); ctx.addNamespace(Constants.DSIG_EC_PREFIX, Constants.DSIG_EC_NS_URI); ctx.addNamespace(Constants.MD_PREFIX, Constants.MD_NS_URI); ctx.addNamespace(Constants.MDP_PREFIX, Constants.MDP_NS_URI); ctx.addNamespace(Constants.MVV_PREFIX, Constants.MVV_NS_URI); ctx.addNamespace(Constants.STB_PREFIX, Constants.STB_NS_URI); ctx.addNamespace(Constants.WRR_PREFIX, Constants.WRR_NS_URI); ctx.addNamespace(Constants.STORK_PREFIX, Constants.STORK_NS_URI); ctx.addNamespace(Constants.STORKP_PREFIX, Constants.STORKP_NS_URI); ctx.addNamespace(Constants.SAML2_PREFIX, Constants.SAML2_NS_URI); ctx.addNamespace(Constants.SAML2P_PREFIX, Constants.SAML2P_NS_URI); ctx.addNamespace(Constants.XENC_PREFIX, Constants.XENC_NS_URI); ctx.addNamespace(Constants.XADES_1_1_1_NS_PREFIX, Constants.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) { MessageProvider msg = MessageProvider.getInstance(); String message = msg.getMessage("xpath.00", new Object[] { exp }); throw new XPathException(message, 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) { MessageProvider msg = MessageProvider.getInstance(); String message = msg.getMessage("xpath.00", new Object[] { exp }); throw new XPathException(message, 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) { MessageProvider msg = MessageProvider.getInstance(); String message = msg.getMessage("xpath.00", new Object[] { exp }); throw new XPathException(message, 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) { MessageProvider msg = MessageProvider.getInstance(); String message = msg.getMessage("xpath.00", new Object[] { exp }); throw new XPathException(message, 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) { MessageProvider msg = MessageProvider.getInstance(); String message = msg.getMessage("xpath.00", new Object[] { exp }); throw new XPathException(message, 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) { MessageProvider msg = MessageProvider.getInstance(); String message = msg.getMessage("xpath.00", new Object[] { exp }); throw new XPathException(message, 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(Constants.SL10_NS_URI)) { sLPrefix = Constants.SL10_PREFIX; } else if (sLNamespace.equals(Constants.SL12_NS_URI)) { sLPrefix = Constants.SL12_PREFIX; } else if (sLNamespace.equals(Constants.SL11_NS_URI)) { sLPrefix = Constants.SL11_PREFIX; } else { MessageProvider msg = MessageProvider.getInstance(); String message = msg.getMessage("xpath.00", new Object[] { "Ung�ltiger Security Layer Namespace: \"" + sLNamespace + "\"."}); throw new XPathException(message, null); } 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, Constants.SL10_PREFIX, Constants.SL10_NS_URI); if (slPrefix == null) { slPrefix = checkSLnsDeclaration(contextElement, Constants.SL11_PREFIX, Constants.SL11_NS_URI); } if (slPrefix == null) { slPrefix = checkSLnsDeclaration(contextElement, Constants.SL12_PREFIX, Constants.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 { MessageProvider msg = MessageProvider.getInstance(); String message = msg.getMessage("xpath.00", new Object[] { "Ung�ltiger SecurityLayer Namespace: \"" + nameSpace + "\"."}); throw new XPathException(message, null); } } } }