Merge from the pain train
[official-gcc.git] / libjava / gnu / xml / xpath / Expr.java
blobfcfae52addc5e517bfbd20fef5ed348567b27684
1 /* Expr.java --
2 Copyright (C) 2004 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., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 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 /* -- 4.1 Node Set Functions -- */
184 * The id function selects elements by their unique ID.
185 * When the argument to id is of type node-set, then the result is
186 * the union of the result of applying id to the string-value of each of
187 * the nodes in the argument node-set. When the argument to id is of any
188 * other type, the argument is converted to a string as if by a call to
189 * the string function; the string is split into a whitespace-separated
190 * list of tokens (whitespace is any sequence of characters matching the
191 * production S); the result is a node-set containing the elements in the
192 * same document as the context node that have a unique ID equal to any of
193 * the tokens in the list.
195 public static Collection _id(Node context, Object object)
197 Set ret = new HashSet();
198 if (object instanceof Collection)
200 Collection nodeSet = (Collection) object;
201 for (Iterator i = nodeSet.iterator(); i.hasNext(); )
203 String string = stringValue((Node) i.next());
204 ret.addAll(_id (context, string));
207 else
209 Document doc = (context instanceof Document) ? (Document) context :
210 context.getOwnerDocument();
211 String string = _string(context, object);
212 StringTokenizer st = new StringTokenizer(string, " \t\r\n");
213 while (st.hasMoreTokens())
215 Node element = doc.getElementById(st.nextToken());
216 if (element != null)
218 ret.add(element);
222 return ret;
226 * The local-name function returns the local part of the expanded-name of
227 * the node in the argument node-set that is first in document order. If
228 * the argument node-set is empty or the first node has no expanded-name,
229 * an empty string is returned. If the argument is omitted, it defaults to
230 * a node-set with the context node as its only member.
232 public static String _local_name(Node context, Collection nodeSet)
234 Node node = (nodeSet == null || nodeSet.size() == 0) ? context :
235 firstNode(nodeSet);
236 return node.getLocalName();
240 * The namespace-uri function returns the namespace URI of the
241 * expanded-name of the node in the argument node-set that is first in
242 * document order. If the argument node-set is empty, the first node has
243 * no expanded-name, or the namespace URI of the expanded-name is null, an
244 * empty string is returned. If the argument is omitted, it defaults to a
245 * node-set with the context node as its only member.
247 public static String _namespace_uri(Node context, Collection nodeSet)
249 Node node = (nodeSet == null || nodeSet.size() == 0) ? context :
250 firstNode(nodeSet);
251 return node.getNamespaceURI();
255 * The name function returns a string containing a QName representing the
256 * expanded-name of the node in the argument node-set that is first in
257 * document order. The QName must represent the expanded-name with respect
258 * to the namespace declarations in effect on the node whose expanded-name
259 * is being represented. Typically, this will be the QName that occurred
260 * in the XML source. This need not be the case if there are namespace
261 * declarations in effect on the node that associate multiple prefixes
262 * with the same namespace. However, an implementation may include
263 * information about the original prefix in its representation of nodes;
264 * in this case, an implementation can ensure that the returned string is
265 * always the same as the QName used in the XML source. If the argument
266 * node-set is empty or the first node has no expanded-name, an empty
267 * string is returned. If the argument it omitted, it defaults to a
268 * node-set with the context node as its only member.
270 public static String _name(Node context, Collection nodeSet)
272 Node node = (nodeSet == null || nodeSet.size() == 0) ? context :
273 firstNode(nodeSet);
274 switch (node.getNodeType())
276 case Node.ATTRIBUTE_NODE:
277 case Node.ELEMENT_NODE:
278 case Node.PROCESSING_INSTRUCTION_NODE:
279 return node.getNodeName();
280 default:
281 return "";
286 * Returns the first node in the set in document order.
288 static Node firstNode(Collection nodeSet)
290 List list = new ArrayList(nodeSet);
291 Collections.sort(list, documentOrderComparator);
292 return (Node) list.get(0);
295 /* -- 4.2 String Functions -- */
298 * Implementation of the XPath <code>string</code> function.
300 public static String _string(Node context, Object object)
302 if (object == null)
304 return stringValue(context);
306 if (object instanceof String)
308 return (String) object;
310 if (object instanceof Boolean)
312 return object.toString();
314 if (object instanceof Double)
316 double d = ((Double) object).doubleValue();
317 if (Double.isNaN(d))
319 return "NaN";
321 else if (d == 0.0d)
323 return "0";
325 else if (Double.isInfinite(d))
327 if (d < 0)
329 return "-Infinity";
331 else
333 return "Infinity";
336 else
338 String ret = decimalFormat.format(d);
339 if (ret.endsWith (".0"))
341 ret = ret.substring(0, ret.length() - 2);
343 return ret;
346 if (object instanceof Collection)
348 Collection nodeSet = (Collection) object;
349 if (nodeSet.isEmpty())
351 return "";
353 Node node = firstNode(nodeSet);
354 return stringValue(node);
356 throw new IllegalArgumentException(object.toString());
359 /* -- 4.3 Boolean Functions -- */
362 * Implementation of the XPath <code>boolean</code> function.
364 public static boolean _boolean(Node context, Object object)
366 if (object instanceof Boolean)
368 return ((Boolean) object).booleanValue();
370 if (object instanceof Double)
372 return ((Double) object).doubleValue() != 0.0;
374 if (object instanceof String)
376 return ((String) object).length() != 0;
378 if (object instanceof Collection)
380 return ((Collection) object).size() != 0;
382 return false; // TODO user defined types
385 /* -- 4.4 Number Functions -- */
388 * Implementation of the XPath <code>number</code> function.
390 public static double _number(Node context, Object object)
392 if (object == null)
394 object = Collections.singleton(context);
396 if (object instanceof Double)
398 return ((Double) object).doubleValue();
400 if (object instanceof Boolean)
402 return ((Boolean) object).booleanValue() ? 1.0 : 0.0;
404 if (object instanceof Collection)
406 // Convert node-set to string
407 object = stringValue((Collection) object);
409 if (object instanceof String)
411 String string = ((String) object).trim();
414 return Double.parseDouble(string);
416 catch (NumberFormatException e)
418 return Double.NaN;
421 return Double.NaN; // TODO user-defined types
425 * Computes the XPath string-value of the specified node-set.
427 public static String stringValue(Collection nodeSet)
429 StringBuffer buf = new StringBuffer();
430 for (Iterator i = nodeSet.iterator(); i.hasNext(); )
432 buf.append(stringValue((Node) i.next()));
434 return buf.toString();
438 * Computes the XPath string-value of the specified node.
440 public static String stringValue(Node node)
442 return stringValue(node, false);
445 static String stringValue(Node node, boolean elementMode)
447 switch (node.getNodeType())
449 case Node.DOCUMENT_NODE: // 5.1 Root Node
450 case Node.DOCUMENT_FRAGMENT_NODE:
451 case Node.ELEMENT_NODE: // 5.2 Element Nodes
452 StringBuffer buf = new StringBuffer();
453 for (Node ctx = node.getFirstChild(); ctx != null;
454 ctx = ctx.getNextSibling())
456 buf.append(stringValue(ctx, true));
458 return buf.toString();
459 case Node.TEXT_NODE: // 5.7 Text Nodes
460 case Node.CDATA_SECTION_NODE:
461 return node.getNodeValue();
462 case Node.ATTRIBUTE_NODE: // 5.3 Attribute Nodes
463 case Node.PROCESSING_INSTRUCTION_NODE: // 5.5 Processing Instruction
464 case Node.COMMENT_NODE: // 5.6 Comment Nodes
465 if (!elementMode)
467 return node.getNodeValue();
469 default:
470 return "";