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)
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
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
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
;
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
;
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
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
;
118 ret
= (Node
) ns
.iterator().next();
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");
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
));
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());
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())
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())
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())
280 Node node
= firstNode(nodeSet
);
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
)
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();
332 else if (Double
.isInfinite(d
))
345 String ret
= decimalFormat
.format(d
);
346 if (ret
.endsWith (".0"))
348 ret
= ret
.substring(0, ret
.length() - 2);
353 if (object
instanceof Collection
)
355 Collection nodeSet
= (Collection
) object
;
356 if (nodeSet
.isEmpty())
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
;
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
)
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
)
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
477 return node
.getNodeValue();
484 static int intValue(Object val
)
486 if (val
instanceof Double
)
488 Double d
= (Double
) val
;
489 return d
.isNaN() ?
0 : d
.intValue();
492 return (int) Math
.ceil(_number(null, val
));