001/*-------------------------------------------------------------------------+ 002| | 003| Copyright 2005-2011 The ConQAT Project | 004| | 005| Licensed under the Apache License, Version 2.0 (the "License"); | 006| you may not use this file except in compliance with the License. | 007| You may obtain a copy of the License at | 008| | 009| http://www.apache.org/licenses/LICENSE-2.0 | 010| | 011| Unless required by applicable law or agreed to in writing, software | 012| distributed under the License is distributed on an "AS IS" BASIS, | 013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 014| See the License for the specific language governing permissions and | 015| limitations under the License. | 016+-------------------------------------------------------------------------*/ 017package org.conqat.lib.commons.xml; 018 019import java.util.Iterator; 020import java.util.List; 021 022import javax.xml.namespace.NamespaceContext; 023import javax.xml.namespace.QName; 024import javax.xml.xpath.XPath; 025import javax.xml.xpath.XPathConstants; 026import javax.xml.xpath.XPathExpressionException; 027import javax.xml.xpath.XPathFactory; 028 029import org.conqat.lib.commons.assertion.CCSMAssert; 030import org.conqat.lib.commons.collections.BidirectionalMap; 031import org.w3c.dom.Element; 032import org.w3c.dom.NodeList; 033 034/** 035 * Evaluator for XPath expression. This is preferable to using the normal 036 * {@link XPath} class as it has built-in support for namespace-handling and the 037 * evaluation-method does not define exceptions. 038 */ 039public class XPathEvaluator { 040 041 /** XPath object used to evaluate Bugzilla result document.s */ 042 private final XPath xPath = XPathFactory.newInstance().newXPath(); 043 044 /** The namespace context to use. */ 045 private final NSContext nsContext; 046 047 /** Create new evaluator. */ 048 public XPathEvaluator() { 049 nsContext = new NSContext(); 050 xPath.setNamespaceContext(nsContext); 051 } 052 053 /** Add a namespace. */ 054 public void addNamespace(String prefix, String uri) { 055 nsContext.addNamespace(prefix, uri); 056 } 057 058 /** 059 * Evaluates an XPath expression on context element. This assumes that the 060 * XPath expression is valid and raises an {@link AssertionError} otherwise. 061 * 062 * @param returnType 063 * use {@link XPathConstants} to define return type. 064 */ 065 public Object select(String expr, Element context, QName returnType) { 066 try { 067 return selectUnsafe(expr, context, returnType); 068 } catch (XPathExpressionException e) { 069 CCSMAssert.fail(e.getMessage()); 070 return null; 071 } 072 } 073 074 /** 075 * Evaluates an XPath expression on context element. 076 * 077 * @param returnType 078 * use {@link XPathConstants} to define return type. 079 * @throws XPathExpressionException 080 * if the XPath expression is invalid. 081 */ 082 public Object selectUnsafe(String expr, Element context, QName returnType) throws XPathExpressionException { 083 return xPath.evaluate(expr, context, returnType); 084 } 085 086 /** 087 * Evaluates an XPath expression on context element. This assumes that the 088 * XPath expression is valid and raises an {@link AssertionError} otherwise. 089 */ 090 public List<Element> selectList(String expr, Element context) { 091 return XMLUtils.elementNodes(selectNodeList(expr, context)); 092 } 093 094 /** 095 * Evaluates an XPath expression on context element. This assumes that the 096 * XPath expression is valid and raises an {@link AssertionError} otherwise. 097 */ 098 public NodeList selectNodeList(String expr, Element context) { 099 return (NodeList) select(expr, context, XPathConstants.NODESET); 100 } 101 102 /** 103 * Evaluates an XPath expression on context element. This assumes that the 104 * XPath expression is valid and raises an {@link AssertionError} otherwise. 105 */ 106 public Element selectElement(String expr, Element context) { 107 return (Element) select(expr, context, XPathConstants.NODE); 108 } 109 110 /** 111 * Evaluates an XPath expression on context element. This assumes that the 112 * XPath expression is valid and raises an {@link AssertionError} otherwise. 113 */ 114 public String selectString(String expr, Element context) { 115 return (String) select(expr, context, XPathConstants.STRING); 116 } 117 118 /** 119 * Evaluates an XPath expression on context element. This assumes that the 120 * XPath expression is valid and raises an {@link AssertionError} otherwise. 121 * Due to the implementation of {@link XPath} this returns 0.0 if the 122 * element was not found. 123 */ 124 public double selectDouble(String expr, Element context) { 125 return (Double) select(expr, context, XPathConstants.NUMBER); 126 } 127 128 /** 129 * Evaluates an XPath expression on context element. This assumes that the 130 * XPath expression is valid and raises an {@link AssertionError} otherwise. 131 * Due to the implementation of {@link XPath} this returns 0 if the element 132 * was not found. 133 */ 134 public int selectInt(String expr, Element context) { 135 return ((Double) select(expr, context, XPathConstants.NUMBER)).intValue(); 136 } 137 138 /** 139 * Evaluates an XPath expression on context element. This assumes that the 140 * XPath expression is valid and raises an {@link AssertionError} otherwise. 141 * Due to the implementation of {@link XPath} this returns 142 * <code>false</code> if the element was not found. 143 */ 144 public boolean selectBoolean(String expr, Element context) { 145 return (Boolean) select(expr, context, XPathConstants.BOOLEAN); 146 } 147 148 /** Simple namespace context. */ 149 private static class NSContext implements NamespaceContext { 150 151 /** Maps from prefix (first) to namespace URI (second). */ 152 private final BidirectionalMap<String, String> map = new BidirectionalMap<String, String>(); 153 154 /** Add new namespace to both maps. */ 155 private void addNamespace(String prefix, String uri) { 156 map.put(prefix, uri); 157 } 158 159 /** {@inheritDoc} */ 160 @Override 161 public String getNamespaceURI(String prefix) { 162 return map.getSecond(prefix); 163 } 164 165 /** {@inheritDoc} */ 166 @Override 167 public String getPrefix(String namespaceURI) { 168 return map.getFirst(namespaceURI); 169 } 170 171 /** {@inheritDoc} */ 172 @Override 173 @SuppressWarnings("rawtypes") 174 public Iterator getPrefixes(String namespaceURI) { 175 return map.getFirstSet().iterator(); 176 } 177 } 178}