1:
37:
38: package ;
39:
40: import ;
41:
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55: import ;
56: import ;
57: import ;
58: import ;
59: import ;
60: import ;
61: import ;
62: import ;
63: import ;
64: import ;
65: import ;
66: import ;
67:
68:
74: public abstract class Expr
75: implements XPathExpression
76: {
77:
78: protected static final Comparator<Node> documentOrderComparator =
79: new DocumentOrderComparator();
80:
81: protected static final DecimalFormat decimalFormat =
82: new DecimalFormat("####################################################" +
83: ".####################################################",
84: new DecimalFormatSymbols(Locale.US));
85:
86: static class ExprNodeSet implements NodeList
87: {
88:
89: private ArrayList<Node> list;
90:
91: ExprNodeSet(Collection<Node> collection)
92: {
93: if (collection instanceof ArrayList)
94: list = (ArrayList<Node>) collection;
95: else
96: list = new ArrayList<Node>(collection);
97: }
98:
99: public int getLength()
100: {
101: return list.size();
102: }
103:
104: public Node item(int index)
105: {
106: try
107: {
108: return list.get(index);
109: }
110: catch (ArrayIndexOutOfBoundsException e)
111: {
112: return null;
113: }
114: }
115:
116: }
117:
118: public Object evaluate(Object item, QName returnType)
119: throws XPathExpressionException
120: {
121: Object ret = null;
122: Node context = null;
123: if (item instanceof Node)
124: {
125: context = (Node) item;
126: ret = evaluate(context, 1, 1);
127: if (XPathConstants.STRING == returnType &&
128: !(ret instanceof String))
129: {
130: ret = _string(context, ret);
131: }
132: else if (XPathConstants.NUMBER == returnType &&
133: !(ret instanceof Double))
134: {
135: ret = new Double(_number(context, ret));
136: }
137: else if (XPathConstants.BOOLEAN == returnType &&
138: !(ret instanceof Boolean))
139: {
140: ret = _boolean(context, ret) ? Boolean.TRUE : Boolean.FALSE;
141: }
142: else if (XPathConstants.NODE == returnType)
143: {
144: if (ret instanceof Collection)
145: {
146:
148: @SuppressWarnings("unchecked")
149: Collection<Node> ns = (Collection<Node>) ret;
150: switch (ns.size())
151: {
152: case 0:
153: ret = null;
154: break;
155: case 1:
156: ret = ns.iterator().next();
157: break;
158: default:
159: throw new XPathExpressionException("multiple nodes in node-set");
160: }
161: }
162: else if (ret != null)
163: {
164: throw new XPathExpressionException("return value is not a node-set");
165: }
166: }
167: else if (XPathConstants.NODESET == returnType)
168: {
169: if (ret != null && !(ret instanceof Collection))
170: {
171: throw new XPathExpressionException("return value is not a node-set");
172: }
173: if (ret != null)
174: {
175:
176: @SuppressWarnings("unchecked")
177: Collection<Node> nodes = (Collection<Node>) ret;
178: ret = new ExprNodeSet(nodes);
179: }
180: }
181: }
182: return ret;
183: }
184:
185: public String evaluate(Object item)
186: throws XPathExpressionException
187: {
188: return (String) evaluate(item, XPathConstants.STRING);
189: }
190:
191: public Object evaluate(InputSource source, QName returnType)
192: throws XPathExpressionException
193: {
194: try
195: {
196: DocumentBuilderFactory factory =
197: new gnu.xml.dom.JAXPFactory();
198: DocumentBuilder builder = factory.newDocumentBuilder();
199: Document doc = builder.parse(source);
200: return evaluate(doc, returnType);
201: }
202: catch (ParserConfigurationException e)
203: {
204: throw new XPathExpressionException(e);
205: }
206: catch (SAXException e)
207: {
208: throw new XPathExpressionException(e);
209: }
210: catch (IOException e)
211: {
212: throw new XPathExpressionException(e);
213: }
214: }
215:
216: public String evaluate(InputSource source)
217: throws XPathExpressionException
218: {
219: return (String) evaluate(source, XPathConstants.STRING);
220: }
221:
222: public abstract Object evaluate(Node context, int pos, int len);
223:
224: public abstract Expr clone(Object context);
225:
226: public abstract boolean references(QName var);
227:
228:
229:
230:
242: public static Collection<Node> _id(Node context, Object object)
243: {
244: Set<Node> ret = new HashSet<Node>();
245: if (object instanceof Collection)
246: {
247:
248: @SuppressWarnings("unchecked")
249: Collection<Node> nodeSet = (Collection<Node>) object;
250: for (Iterator<Node> i = nodeSet.iterator(); i.hasNext(); )
251: {
252: String string = stringValue(i.next());
253: ret.addAll(_id (context, string));
254: }
255: }
256: else
257: {
258: Document doc = (context instanceof Document) ? (Document) context :
259: context.getOwnerDocument();
260: String string = _string(context, object);
261: StringTokenizer st = new StringTokenizer(string, " \t\r\n");
262: while (st.hasMoreTokens())
263: {
264: Node element = doc.getElementById(st.nextToken());
265: if (element != null)
266: {
267: ret.add(element);
268: }
269: }
270: }
271: return ret;
272: }
273:
274:
281: public static String _local_name(Node context, Collection<Node> nodeSet)
282: {
283: if (nodeSet == null || nodeSet.isEmpty())
284: return "";
285: Node node = firstNode(nodeSet);
286: String ret = node.getLocalName();
287: return (ret == null) ? "" : ret;
288: }
289:
290:
298: public static String _namespace_uri(Node context, Collection<Node> nodeSet)
299: {
300: if (nodeSet == null || nodeSet.isEmpty())
301: return "";
302: Node node = firstNode(nodeSet);
303: String ret = node.getNamespaceURI();
304: return (ret == null) ? "" : ret;
305: }
306:
307:
323: public static String _name(Node context, Collection<Node> nodeSet)
324: {
325: if (nodeSet == null || nodeSet.isEmpty())
326: return "";
327: Node node = firstNode(nodeSet);
328: String ret = null;
329: switch (node.getNodeType())
330: {
331: case Node.ATTRIBUTE_NODE:
332: case Node.ELEMENT_NODE:
333: case Node.PROCESSING_INSTRUCTION_NODE:
334: ret = node.getNodeName();
335: }
336: return (ret == null) ? "" : ret;
337: }
338:
339:
342: static Node firstNode(Collection<Node> nodeSet)
343: {
344: List<Node> list = new ArrayList<Node>(nodeSet);
345: Collections.sort(list, documentOrderComparator);
346: return list.get(0);
347: }
348:
349:
350:
351:
354: public static String _string(Node context, Object object)
355: {
356: if (object == null)
357: {
358: return stringValue(context);
359: }
360: if (object instanceof String)
361: {
362: return (String) object;
363: }
364: if (object instanceof Boolean)
365: {
366: return object.toString();
367: }
368: if (object instanceof Double)
369: {
370: double d = ((Double) object).doubleValue();
371: if (Double.isNaN(d))
372: {
373: return "NaN";
374: }
375: else if (d == 0.0d)
376: {
377: return "0";
378: }
379: else if (Double.isInfinite(d))
380: {
381: if (d < 0)
382: {
383: return "-Infinity";
384: }
385: else
386: {
387: return "Infinity";
388: }
389: }
390: else
391: {
392: String ret = decimalFormat.format(d);
393: if (ret.endsWith (".0"))
394: {
395: ret = ret.substring(0, ret.length() - 2);
396: }
397: return ret;
398: }
399: }
400: if (object instanceof Collection)
401: {
402:
404: @SuppressWarnings("unchecked")
405: Collection<Node> nodeSet = (Collection<Node>) object;
406: if (nodeSet.isEmpty())
407: {
408: return "";
409: }
410: Node node = firstNode(nodeSet);
411: return stringValue(node);
412: }
413: throw new IllegalArgumentException(object.toString());
414: }
415:
416:
417:
418:
421: public static boolean _boolean(Node context, Object object)
422: {
423: if (object instanceof Boolean)
424: {
425: return ((Boolean) object).booleanValue();
426: }
427: if (object instanceof Double)
428: {
429: Double value = (Double) object;
430: if (value.isNaN())
431: return false;
432: return value.doubleValue() != 0.0;
433: }
434: if (object instanceof String)
435: {
436: return ((String) object).length() != 0;
437: }
438: if (object instanceof Collection)
439: {
440: return ((Collection<?>) object).size() != 0;
441: }
442: return false;
443: }
444:
445:
446:
447:
450: public static double _number(Node context, Object object)
451: {
452: if (object == null)
453: {
454: object = Collections.singleton(context);
455: }
456: if (object instanceof Double)
457: {
458: return ((Double) object).doubleValue();
459: }
460: if (object instanceof Boolean)
461: {
462: return ((Boolean) object).booleanValue() ? 1.0 : 0.0;
463: }
464: if (object instanceof Collection)
465: {
466:
468: @SuppressWarnings("unchecked")
469: Collection<Node> nodeSet = (Collection<Node>) object;
470:
471: object = stringValue(nodeSet);
472: }
473: if (object instanceof String)
474: {
475: String string = ((String) object).trim();
476: try
477: {
478: return Double.parseDouble(string);
479: }
480: catch (NumberFormatException e)
481: {
482: return Double.NaN;
483: }
484: }
485: return Double.NaN;
486: }
487:
488:
491: public static String stringValue(Collection<Node> nodeSet)
492: {
493: CPStringBuilder buf = new CPStringBuilder();
494: for (Iterator<Node> i = nodeSet.iterator(); i.hasNext(); )
495: {
496: buf.append(stringValue(i.next()));
497: }
498: return buf.toString();
499: }
500:
501:
504: public static String stringValue(Node node)
505: {
506: return stringValue(node, false);
507: }
508:
509: static String stringValue(Node node, boolean elementMode)
510: {
511: switch (node.getNodeType())
512: {
513: case Node.DOCUMENT_NODE:
514: case Node.DOCUMENT_FRAGMENT_NODE:
515: case Node.ELEMENT_NODE:
516: CPStringBuilder buf = new CPStringBuilder();
517: for (Node ctx = node.getFirstChild(); ctx != null;
518: ctx = ctx.getNextSibling())
519: {
520: buf.append(stringValue(ctx, true));
521: }
522: return buf.toString();
523: case Node.TEXT_NODE:
524: case Node.CDATA_SECTION_NODE:
525: return node.getNodeValue();
526: case Node.ATTRIBUTE_NODE:
527: case Node.PROCESSING_INSTRUCTION_NODE:
528: case Node.COMMENT_NODE:
529: if (!elementMode)
530: {
531: return node.getNodeValue();
532: }
533: default:
534: return "";
535: }
536: }
537:
538: static int intValue(Object val)
539: {
540: if (val instanceof Double)
541: {
542: Double d = (Double) val;
543: return d.isNaN() ? 0 : d.intValue();
544: }
545: else
546: return (int) Math.ceil(_number(null, val));
547: }
548:
549: }