Merge from mainline
[official-gcc.git] / libjava / classpath / gnu / xml / xpath / Expr.java
blob76fd49eeff5870ff576320c1926fece6aa68019e
1 /* Expr.java --
2 Copyright (C) 2004,2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 package gnu.xml.xpath;
40 import java.io.IOException;
41 import java.text.DecimalFormat;
42 import java.text.DecimalFormatSymbols;
43 import java.util.ArrayList;
44 import java.util.Collection;
45 import java.util.Collections;
46 import java.util.Comparator;
47 import java.util.HashSet;
48 import java.util.Iterator;
49 import java.util.List;
50 import java.util.Locale;
51 import java.util.Set;
52 import java.util.StringTokenizer;
53 import javax.xml.namespace.QName;
54 import javax.xml.parsers.DocumentBuilder;
55 import javax.xml.parsers.DocumentBuilderFactory;
56 import javax.xml.parsers.ParserConfigurationException;
57 import javax.xml.xpath.XPathConstants;
58 import javax.xml.xpath.XPathExpression;
59 import javax.xml.xpath.XPathExpressionException;
60 import org.w3c.dom.Document;
61 import org.w3c.dom.Node;
62 import org.xml.sax.InputSource;
63 import org.xml.sax.SAXException;
65 /**
66 * An XPath expression.
67 * This can be evaluated in the context of a node to produce a result.
69 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
71 public abstract class Expr
72 implements XPathExpression
75 protected static final Comparator documentOrderComparator =
76 new DocumentOrderComparator();
78 protected static final DecimalFormat decimalFormat =
79 new DecimalFormat("####################################################" +
80 ".####################################################",
81 new DecimalFormatSymbols(Locale.US));
83 public Object evaluate(Object item, QName returnType)
84 throws XPathExpressionException
86 Object ret = null;
87 Node context = null;
88 if (item instanceof Node)
90 context = (Node) item;
91 ret = evaluate(context, 1, 1);
92 if (XPathConstants.STRING == returnType &&
93 !(ret instanceof String))
95 ret = _string(context, ret);
97 else if (XPathConstants.NUMBER == returnType &&
98 !(ret instanceof Double))
100 ret = new Double(_number(context, ret));
102 else if (XPathConstants.BOOLEAN == returnType &&
103 !(ret instanceof Boolean))
105 ret = _boolean(context, ret) ? Boolean.TRUE : Boolean.FALSE;
107 else if (XPathConstants.NODE == returnType)
109 if (ret instanceof Collection)
111 Collection ns = (Collection) ret;
112 switch (ns.size())
114 case 0:
115 ret = null;
116 break;
117 case 1:
118 ret = (Node) ns.iterator().next();
119 break;
120 default:
121 throw new XPathExpressionException("multiple nodes in node-set");
124 else if (ret != null)
126 throw new XPathExpressionException("return value is not a node-set");
129 else if (XPathConstants.NODESET == returnType)
131 if (ret != null && !(ret instanceof Collection))
133 throw new XPathExpressionException("return value is not a node-set");
137 return ret;
140 public String evaluate(Object item)
141 throws XPathExpressionException
143 return (String) evaluate(item, XPathConstants.STRING);
146 public Object evaluate(InputSource source, QName returnType)
147 throws XPathExpressionException
151 DocumentBuilderFactory factory =
152 new gnu.xml.dom.JAXPFactory();
153 DocumentBuilder builder = factory.newDocumentBuilder();
154 Document doc = builder.parse(source);
155 return evaluate(doc, returnType);
157 catch (ParserConfigurationException e)
159 throw new XPathExpressionException(e);
161 catch (SAXException e)
163 throw new XPathExpressionException(e);
165 catch (IOException e)
167 throw new XPathExpressionException(e);
171 public String evaluate(InputSource source)
172 throws XPathExpressionException
174 return (String) evaluate(source, XPathConstants.STRING);
177 public abstract Object evaluate(Node context, int pos, int len);
179 public abstract Expr clone(Object context);
181 public abstract boolean references(QName var);
183 /* -- 4.1 Node Set Functions -- */
186 * The id function selects elements by their unique ID.
187 * When the argument to id is of type node-set, then the result is
188 * the union of the result of applying id to the string-value of each of
189 * the nodes in the argument node-set. When the argument to id is of any
190 * other type, the argument is converted to a string as if by a call to
191 * the string function; the string is split into a whitespace-separated
192 * list of tokens (whitespace is any sequence of characters matching the
193 * production S); the result is a node-set containing the elements in the
194 * same document as the context node that have a unique ID equal to any of
195 * the tokens in the list.
197 public static Collection _id(Node context, Object object)
199 Set ret = new HashSet();
200 if (object instanceof Collection)
202 Collection nodeSet = (Collection) object;
203 for (Iterator i = nodeSet.iterator(); i.hasNext(); )
205 String string = stringValue((Node) i.next());
206 ret.addAll(_id (context, string));
209 else
211 Document doc = (context instanceof Document) ? (Document) context :
212 context.getOwnerDocument();
213 String string = _string(context, object);
214 StringTokenizer st = new StringTokenizer(string, " \t\r\n");
215 while (st.hasMoreTokens())
217 Node element = doc.getElementById(st.nextToken());
218 if (element != null)
220 ret.add(element);
224 return ret;
228 * The local-name function returns the local part of the expanded-name of
229 * the node in the argument node-set that is first in document order. If
230 * the argument node-set is empty or the first node has no expanded-name,
231 * an empty string is returned. If the argument is omitted, it defaults to
232 * a node-set with the context node as its only member.
234 public static String _local_name(Node context, Collection nodeSet)
236 if (nodeSet == null || nodeSet.isEmpty())
237 return "";
238 Node node = firstNode(nodeSet);
239 String ret = node.getLocalName();
240 return (ret == null) ? "" : ret;
244 * The namespace-uri function returns the namespace URI of the
245 * expanded-name of the node in the argument node-set that is first in
246 * document order. If the argument node-set is empty, the first node has
247 * no expanded-name, or the namespace URI of the expanded-name is null, an
248 * empty string is returned. If the argument is omitted, it defaults to a
249 * node-set with the context node as its only member.
251 public static String _namespace_uri(Node context, Collection nodeSet)
253 if (nodeSet == null || nodeSet.isEmpty())
254 return "";
255 Node node = firstNode(nodeSet);
256 String ret = node.getNamespaceURI();
257 return (ret == null) ? "" : ret;
261 * The name function returns a string containing a QName representing the
262 * expanded-name of the node in the argument node-set that is first in
263 * document order. The QName must represent the expanded-name with respect
264 * to the namespace declarations in effect on the node whose expanded-name
265 * is being represented. Typically, this will be the QName that occurred
266 * in the XML source. This need not be the case if there are namespace
267 * declarations in effect on the node that associate multiple prefixes
268 * with the same namespace. However, an implementation may include
269 * information about the original prefix in its representation of nodes;
270 * in this case, an implementation can ensure that the returned string is
271 * always the same as the QName used in the XML source. If the argument
272 * node-set is empty or the first node has no expanded-name, an empty
273 * string is returned. If the argument it omitted, it defaults to a
274 * node-set with the context node as its only member.
276 public static String _name(Node context, Collection nodeSet)
278 if (nodeSet == null || nodeSet.isEmpty())
279 return "";
280 Node node = firstNode(nodeSet);
281 String ret = null;
282 switch (node.getNodeType())
284 case Node.ATTRIBUTE_NODE:
285 case Node.ELEMENT_NODE:
286 case Node.PROCESSING_INSTRUCTION_NODE:
287 ret = node.getNodeName();
289 return (ret == null) ? "" : ret;
293 * Returns the first node in the set in document order.
295 static Node firstNode(Collection nodeSet)
297 List list = new ArrayList(nodeSet);
298 Collections.sort(list, documentOrderComparator);
299 return (Node) list.get(0);
302 /* -- 4.2 String Functions -- */
305 * Implementation of the XPath <code>string</code> function.
307 public static String _string(Node context, Object object)
309 if (object == null)
311 return stringValue(context);
313 if (object instanceof String)
315 return (String) object;
317 if (object instanceof Boolean)
319 return object.toString();
321 if (object instanceof Double)
323 double d = ((Double) object).doubleValue();
324 if (Double.isNaN(d))
326 return "NaN";
328 else if (d == 0.0d)
330 return "0";
332 else if (Double.isInfinite(d))
334 if (d < 0)
336 return "-Infinity";
338 else
340 return "Infinity";
343 else
345 String ret = decimalFormat.format(d);
346 if (ret.endsWith (".0"))
348 ret = ret.substring(0, ret.length() - 2);
350 return ret;
353 if (object instanceof Collection)
355 Collection nodeSet = (Collection) object;
356 if (nodeSet.isEmpty())
358 return "";
360 Node node = firstNode(nodeSet);
361 return stringValue(node);
363 throw new IllegalArgumentException(object.toString());
366 /* -- 4.3 Boolean Functions -- */
369 * Implementation of the XPath <code>boolean</code> function.
371 public static boolean _boolean(Node context, Object object)
373 if (object instanceof Boolean)
375 return ((Boolean) object).booleanValue();
377 if (object instanceof Double)
379 Double value = (Double) object;
380 if (value.isNaN())
381 return false;
382 return value.doubleValue() != 0.0;
384 if (object instanceof String)
386 return ((String) object).length() != 0;
388 if (object instanceof Collection)
390 return ((Collection) object).size() != 0;
392 return false; // TODO user defined types
395 /* -- 4.4 Number Functions -- */
398 * Implementation of the XPath <code>number</code> function.
400 public static double _number(Node context, Object object)
402 if (object == null)
404 object = Collections.singleton(context);
406 if (object instanceof Double)
408 return ((Double) object).doubleValue();
410 if (object instanceof Boolean)
412 return ((Boolean) object).booleanValue() ? 1.0 : 0.0;
414 if (object instanceof Collection)
416 // Convert node-set to string
417 object = stringValue((Collection) object);
419 if (object instanceof String)
421 String string = ((String) object).trim();
424 return Double.parseDouble(string);
426 catch (NumberFormatException e)
428 return Double.NaN;
431 return Double.NaN; // TODO user-defined types
435 * Computes the XPath string-value of the specified node-set.
437 public static String stringValue(Collection nodeSet)
439 StringBuffer buf = new StringBuffer();
440 for (Iterator i = nodeSet.iterator(); i.hasNext(); )
442 buf.append(stringValue((Node) i.next()));
444 return buf.toString();
448 * Computes the XPath string-value of the specified node.
450 public static String stringValue(Node node)
452 return stringValue(node, false);
455 static String stringValue(Node node, boolean elementMode)
457 switch (node.getNodeType())
459 case Node.DOCUMENT_NODE: // 5.1 Root Node
460 case Node.DOCUMENT_FRAGMENT_NODE:
461 case Node.ELEMENT_NODE: // 5.2 Element Nodes
462 StringBuffer buf = new StringBuffer();
463 for (Node ctx = node.getFirstChild(); ctx != null;
464 ctx = ctx.getNextSibling())
466 buf.append(stringValue(ctx, true));
468 return buf.toString();
469 case Node.TEXT_NODE: // 5.7 Text Nodes
470 case Node.CDATA_SECTION_NODE:
471 return node.getNodeValue();
472 case Node.ATTRIBUTE_NODE: // 5.3 Attribute Nodes
473 case Node.PROCESSING_INSTRUCTION_NODE: // 5.5 Processing Instruction
474 case Node.COMMENT_NODE: // 5.6 Comment Nodes
475 if (!elementMode)
477 return node.getNodeValue();
479 default:
480 return "";
484 static int intValue(Object val)
486 if (val instanceof Double)
488 Double d = (Double) val;
489 return d.isNaN() ? 0 : d.intValue();
491 else
492 return (int) Math.ceil(_number(null, val));