View Javadoc

1   /*
2    * Copyright 2010 Capgemini
3    * Licensed under the Apache License, Version 2.0 (the "License"); 
4    * you may not use this file except in compliance with the License. 
5    * You may obtain a copy of the License at 
6    * 
7    * http://www.apache.org/licenses/LICENSE-2.0 
8    * 
9    * Unless required by applicable law or agreed to in writing, software 
10   * distributed under the License is distributed on an "AS IS" BASIS, 
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
12   * See the License for the specific language governing permissions and 
13   * limitations under the License. 
14   * 
15   */
16  package org.xmlfield.core.impl.dom;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  import java.util.Map;
21  import java.util.Map.Entry;
22  
23  import org.apache.commons.lang.StringUtils;
24  import org.jaxen.JaxenException;
25  import org.jaxen.XPath;
26  import org.jaxen.dom.DOMXPath;
27  import org.w3c.dom.Node;
28  import org.xmlfield.core.api.XmlFieldNode;
29  import org.xmlfield.core.api.XmlFieldNodeList;
30  import org.xmlfield.core.api.XmlFieldSelector;
31  import org.xmlfield.core.exception.XmlFieldXPathException;
32  import org.xmlfield.core.internal.NamespaceMap;
33  
34  import com.google.common.collect.MapMaker;
35  
36  /**
37   * Default xml field selector implementation. Use the jaxp implementation.
38   * <p>
39   * DomJaxenSelector is thread safe.
40   * 
41   * @author Guillaume Mary <guillaume.mary@capgemini.com>
42   * 
43   */
44  public class DomJaxenSelector implements XmlFieldSelector {
45  
46  	/**
47  	 * Enable cache for XPath objects. Warning : this is for testing only. XPath
48  	 * objects are not always thread safe. You should not enable caching without
49  	 * knowing EXACTLY what you are doing.
50  	 * 
51  	 * @see http://sourceforge.net/apps/mantisbt/xmlfield/view.php?id=41
52  	 */
53  	static boolean useCache = false;
54  
55  	public static XPath addNamespace(final NamespaceMap namespaces, XPath xp)
56  			throws JaxenException {
57  		if (namespaces != null) {
58  			for (Entry<String, String> entry : namespaces) {
59  				xp.addNamespace(entry.getKey(), entry.getValue());
60  			}
61  		}
62  		return xp;
63  	}
64  
65  	/**
66  	 * Cache for XPath selectors. Uses soft values to let the garbage collector
67  	 * free unused selectors.
68  	 */
69  	private Map<String, XPath> xpathCache = new MapMaker().softValues()
70  			.makeMap();
71  
72  	private void checkXPathNotNull(String xpath) throws XmlFieldXPathException {
73  		if (xpath == null) {
74  			throw new XmlFieldXPathException("The requested xpath is null");
75  		}
76  	}
77  
78  	/**
79  	 * Get XPath selector, trying to reuse an item from the cache or create a
80  	 * new one.
81  	 * 
82  	 * @param namespaces
83  	 * @param xpath
84  	 * @return
85  	 * @throws JaxenException
86  	 */
87  	private XPath getXPath(NamespaceMap namespaces, String xpath)
88  			throws JaxenException {
89  		XPath result = null;
90  		if (useCache) {
91  			// Build cache key
92  			String key = StringUtils.EMPTY;
93  			if (namespaces != null) {
94  				key = key + namespaces.toString();
95  			}
96  			key = key + "|" + xpath;
97  
98  			// Get from cache
99  			result = xpathCache.get(key);
100 
101 			// If not in cache, create new selector and add it to the cache.
102 			if (result == null) {
103 				result = new DOMXPath(xpath);
104 				addNamespace(namespaces, result);
105 				xpathCache.put(key, result);
106 			}
107 		} else {
108 			result = new DOMXPath(xpath);
109 			addNamespace(namespaces, result);
110 		}
111 		return result;
112 	}
113 
114 	@Override
115 	public Boolean selectXPathToBoolean(NamespaceMap namespaces, String xpath,
116 			XmlFieldNode node) throws XmlFieldXPathException {
117 		checkXPathNotNull(xpath);
118 		final Boolean value;
119 		try {
120 			final XPath xp = getXPath(namespaces, xpath);
121 			value = xp.booleanValueOf(node.getNode());
122 		} catch (JaxenException e) {
123 			throw new XmlFieldXPathException(e);
124 		}
125 		return value;
126 	}
127 
128 	@Override
129 	public XmlFieldNode selectXPathToNode(NamespaceMap namespaces,
130 			String xpath, XmlFieldNode node) throws XmlFieldXPathException {
131 		checkXPathNotNull(xpath);
132 		final Node value;
133 		try {
134 			final XPath xp = getXPath(namespaces, xpath);
135 			value = (Node) xp.selectSingleNode(node.getNode());
136 		} catch (JaxenException e) {
137 			throw new XmlFieldXPathException(e);
138 		}
139 		if (value == null) {
140 			return null;
141 		}
142 		return new DomNode(value);
143 	}
144 
145 	@SuppressWarnings("unchecked")
146 	@Override
147 	public XmlFieldNodeList selectXPathToNodeList(NamespaceMap namespaces,
148 			String xpath, XmlFieldNode node) throws XmlFieldXPathException {
149 		checkXPathNotNull(xpath);
150 		final List<Node> values;
151 		try {
152 			final XPath xp = getXPath(namespaces, xpath);
153 			values = xp.selectNodes(node.getNode());
154 		} catch (JaxenException e) {
155 			throw new XmlFieldXPathException(e);
156 		}
157 		final int nodeCount = values.size();
158 
159 		final List<XmlFieldNode> list = new ArrayList<XmlFieldNode>();
160 
161 		for (int i = 0; i < nodeCount; ++i) {
162 
163 			final XmlFieldNode subNode = new DomNode(values.get(i));
164 
165 			list.add(subNode);
166 		}
167 		return new DomNodeList(list);
168 	}
169 
170 	@Override
171 	public Double selectXPathToNumber(NamespaceMap namespaces, String xpath,
172 			XmlFieldNode node) throws XmlFieldXPathException {
173 		checkXPathNotNull(xpath);
174 		final Double value;
175 		try {
176 			final XPath xp = getXPath(namespaces, xpath);
177 			value = xp.numberValueOf(node.getNode()).doubleValue();
178 		} catch (JaxenException e) {
179 			throw new XmlFieldXPathException(e);
180 		}
181 		return value;
182 	}
183 
184 	@Override
185 	public String selectXPathToString(NamespaceMap namespaces, String xpath,
186 			XmlFieldNode node) throws XmlFieldXPathException {
187 		checkXPathNotNull(xpath);
188 		final String value;
189 		try {
190 			final XPath xp = getXPath(namespaces, xpath);
191 			value = xp.stringValueOf(node.getNode());
192 		} catch (JaxenException e) {
193 			throw new XmlFieldXPathException(e);
194 		}
195 		return value;
196 	}
197 
198 }