001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.xpath;
030:
031: import com.caucho.util.CharBuffer;
032: import com.caucho.xml.XmlChar;
033: import com.caucho.xml.XmlUtil;
034: import com.caucho.xpath.expr.ObjectVar;
035: import com.caucho.xpath.expr.Var;
036: import com.caucho.xpath.pattern.AbstractPattern;
037: import com.caucho.xpath.pattern.FromExpr;
038: import com.caucho.xpath.pattern.NodeArrayListIterator;
039: import com.caucho.xpath.pattern.NodeIterator;
040: import com.caucho.xpath.pattern.NodeListIterator;
041: import com.caucho.xpath.pattern.SingleNodeIterator;
042:
043: import org.w3c.dom.Node;
044: import org.w3c.dom.NodeList;
045:
046: import java.util.ArrayList;
047: import java.util.Iterator;
048:
049: /**
050: * Compute values from nodes. Because the expressions themselves are
051: * untyped, the class provides methods for creating the type of the
052: * desired result.
053: */
054: abstract public class Expr {
055: protected static final int CONST = 0;
056:
057: protected static final int NODE_SET = CONST + 1;
058: protected static final int ID = NODE_SET + 1;
059:
060: protected static final int OR = ID + 1;
061: protected static final int AND = OR + 1;
062:
063: protected static final int EQ = AND + 1;
064: protected static final int NEQ = EQ + 1;
065: protected static final int LT = NEQ + 1;
066: protected static final int LE = LT + 1;
067: protected static final int GT = LE + 1;
068: protected static final int GE = GT + 1;
069:
070: protected static final int BOOLEAN_EQ = GE + 1;
071: protected static final int BOOLEAN_NEQ = BOOLEAN_EQ + 1;
072:
073: protected static final int NUMBER_EQ = BOOLEAN_NEQ + 1;
074: protected static final int NUMBER_NEQ = NUMBER_EQ + 1;
075: protected static final int NUMBER_LT = NUMBER_NEQ + 1;
076: protected static final int NUMBER_LE = NUMBER_LT + 1;
077: protected static final int NUMBER_GT = NUMBER_LE + 1;
078: protected static final int NUMBER_GE = NUMBER_GT + 1;
079:
080: protected static final int STRING_EQ = NUMBER_GE + 1;
081: protected static final int STRING_NEQ = STRING_EQ + 1;
082:
083: protected static final int NEG = STRING_NEQ + 1;
084: protected static final int ADD = NEG + 1;
085: protected static final int SUB = ADD + 1;
086: protected static final int MUL = SUB + 1;
087: protected static final int DIV = MUL + 1;
088: protected static final int QUO = DIV + 1;
089: protected static final int MOD = QUO + 1;
090:
091: protected static final int TRUE = MOD + 1;
092: protected static final int FALSE = TRUE + 1;
093: protected static final int NOT = FALSE + 1;
094: protected static final int BOOLEAN = NOT + 1;
095: protected static final int LANG = BOOLEAN + 1;
096:
097: protected static final int NUMBER = LANG + 1;
098: protected static final int SUM = NUMBER + 1;
099: protected static final int FLOOR = SUM + 1;
100: protected static final int CEILING = FLOOR + 1;
101: protected static final int ROUND = CEILING + 1;
102: public static final int POSITION = ROUND + 1;
103: protected static final int COUNT = POSITION + 1;
104: protected static final int LAST = COUNT + 1;
105:
106: protected static final int STRING = LAST + 1;
107: protected static final int CONCAT = STRING + 1;
108: protected static final int STARTS_WITH = CONCAT + 1;
109: protected static final int CONTAINS = STARTS_WITH + 1;
110: protected static final int SUBSTRING = CONTAINS + 1;
111: protected static final int SUBSTRING_BEFORE = SUBSTRING + 1;
112: protected static final int SUBSTRING_AFTER = SUBSTRING_BEFORE + 1;
113: protected static final int STRING_LENGTH = SUBSTRING_AFTER + 1;
114: protected static final int NORMALIZE = STRING_LENGTH + 1;
115: protected static final int TRANSLATE = NORMALIZE + 1;
116: protected static final int FORMAT_NUMBER = TRANSLATE + 1;
117:
118: protected static final int LOCAL_PART = FORMAT_NUMBER + 1;
119: protected static final int NAMESPACE = LOCAL_PART + 1;
120: protected static final int QNAME = NAMESPACE + 1;
121: protected static final int GENERATE_ID = QNAME + 1;
122:
123: protected static final int FUNCTION_AVAILABLE = GENERATE_ID + 1;
124: protected static final int SYSTEM_PROPERTY = FUNCTION_AVAILABLE + 1;
125:
126: protected static final int IF = SYSTEM_PROPERTY + 1;
127:
128: protected static final int SELF = IF + 1;
129: protected static final int SELF_NAME = SELF + 1;
130: protected static final int ATTRIBUTE = SELF_NAME + 1;
131: protected static final int ELEMENT = ATTRIBUTE + 1;
132:
133: protected static final int BASE_URI = ELEMENT + 1;
134: protected static final int LAST_FUN = BASE_URI + 1;
135:
136: private AbstractPattern listContext;
137:
138: protected Expr() {
139: }
140:
141: public void setListContext(AbstractPattern listContext) {
142: this .listContext = listContext;
143: }
144:
145: public AbstractPattern getListContext() {
146: return listContext;
147: }
148:
149: /**
150: * true if the expression prefers to return a number.
151: */
152: public boolean isNumber() {
153: return false;
154: }
155:
156: /**
157: * Evaluates the expression as a double using the node as a context.
158: *
159: * @param node the node to evaluate and use as a context
160: *
161: * @return the numeric value.
162: */
163: public double evalNumber(Node node) throws XPathException {
164: Env env = XPath.createEnv();
165: env.setCurrentNode(node);
166: env.setContextNode(node);
167:
168: double result = evalNumber(node, env);
169:
170: XPath.freeEnv(env);
171:
172: return result;
173: }
174:
175: /**
176: * Evaluates the expression as a number.
177: *
178: * @param node the current node.
179: * @param env variable environment.
180: *
181: * @return the numeric value.
182: */
183: public abstract double evalNumber(Node node, ExprEnvironment env)
184: throws XPathException;
185:
186: /**
187: * true if the expression prefers to return a boolean.
188: */
189: public boolean isBoolean() {
190: return false;
191: }
192:
193: /**
194: * Returns the boolean value of the node.
195: *
196: * @param node the node to evaluate and use as a context
197: *
198: * @return the boolean value
199: */
200: public boolean evalBoolean(Node node) throws XPathException {
201: Env env = XPath.createEnv();
202: env.setCurrentNode(node);
203: env.setContextNode(node);
204:
205: boolean result = evalBoolean(node, env);
206:
207: XPath.freeEnv(env);
208:
209: return result;
210: }
211:
212: /**
213: * Returns the boolean value of the node.
214: *
215: * @param node the node to evaluate and use as a context
216: * @param env variable environment.
217: *
218: * @return the boolean value.
219: */
220: public abstract boolean evalBoolean(Node node, ExprEnvironment env)
221: throws XPathException;
222:
223: /**
224: * Returns the expression evaluated as a string.
225: *
226: * @param node the node to evaluate and use as a context
227: *
228: * @return the string value of the expression.
229: */
230: public String evalString(Node node) throws XPathException {
231: Env env = XPath.createEnv();
232: env.setCurrentNode(node);
233: env.setContextNode(node);
234:
235: String result = evalString(node, env);
236:
237: XPath.freeEnv(env);
238:
239: return result;
240: }
241:
242: /**
243: * true if the expression prefers to return a string.
244: */
245: public boolean isString() {
246: return false;
247: }
248:
249: /**
250: * Returns the string value of the node.
251: *
252: * @param node the node to evaluate and use as a context
253: * @param env variable environment.
254: */
255: public abstract String evalString(Node node, ExprEnvironment env)
256: throws XPathException;
257:
258: /**
259: * Fills a char buffer with the evaluated string results.
260: *
261: * @param cb the buffer containing the results.
262: * @param node the node to evaluate and use as a context
263: */
264: public void evalString(CharBuffer cb, Node node)
265: throws XPathException {
266: Env env = XPath.createEnv();
267: env.setCurrentNode(node);
268: env.setContextNode(node);
269:
270: evalString(cb, node, env);
271:
272: XPath.freeEnv(env);
273: }
274:
275: /**
276: * Fills a char buffer with the evaluated string results.
277: *
278: * @param cb the buffer containing the results.
279: * @param node the node to evaluate and use as a context
280: * @param env the variable environment
281: */
282: public void evalString(CharBuffer cb, Node node, ExprEnvironment env)
283: throws XPathException {
284: cb.append(evalString(node, env));
285: }
286:
287: /**
288: * true if the expression prefers to return a node set.
289: */
290: public boolean isNodeSet() {
291: return false;
292: }
293:
294: /**
295: * Returns an iterator of matching nodes
296: *
297: * @param node the node to evaluate and use as a context
298: *
299: * @return the value as a node iterator.
300: */
301: public NodeIterator evalNodeSet(Node node) throws XPathException {
302: Env env = XPath.createEnv();
303: env.setCurrentNode(node);
304: env.setContextNode(node);
305:
306: NodeIterator result = evalNodeSet(node, env);
307:
308: XPath.freeEnv(env);
309:
310: return result;
311: }
312:
313: /**
314: * Returns an iterator of matching nodes
315: *
316: * @param node the node to evaluate and use as a context
317: * @param env variable environment.
318: *
319: * @return the value as a node iterator.
320: */
321: public NodeIterator evalNodeSet(Node node, ExprEnvironment env)
322: throws XPathException {
323: Object obj = evalObject(node, env);
324:
325: if (obj instanceof Node)
326: return new SingleNodeIterator(env, (Node) obj);
327:
328: else if (obj instanceof NodeList)
329: return new NodeListIterator(env, (NodeList) obj);
330:
331: else if (obj instanceof NodeIterator)
332: return (NodeIterator) obj;
333:
334: else if (obj instanceof ArrayList)
335: return new NodeArrayListIterator(env, (ArrayList) obj);
336:
337: else {
338: return new SingleNodeIterator(env, null);
339: }
340: }
341:
342: /**
343: * Returns the object value of the node.
344: *
345: * @param node the node to evaluate and use as a context
346: */
347: public Object evalObject(Node node) throws XPathException {
348: Env env = XPath.createEnv();
349: env.setCurrentNode(node);
350: env.setContextNode(node);
351:
352: Object result = evalObject(node, env);
353:
354: XPath.freeEnv(env);
355:
356: return result;
357: }
358:
359: /**
360: * Returns the object value of the node.
361: *
362: * @param node the node to evaluate and use as a context
363: * @param env variable environment.
364: */
365: public abstract Object evalObject(Node node, ExprEnvironment env)
366: throws XPathException;
367:
368: /**
369: * Evaluates to a variable.
370: *
371: * @param node the node to evaluate and use as a context.
372: * @param env the variable environment.
373: *
374: * @return a variable containing the value.
375: */
376: public Var evalVar(Node node, ExprEnvironment env)
377: throws XPathException {
378: Object obj = evalObject(node, env);
379:
380: return new ObjectVar(obj);
381: }
382:
383: /**
384: * Adds a variable with the expression's value.
385: */
386: public void addVar(Env newEnv, String name, Node node, Env env)
387: throws XPathException {
388: Var var = evalVar(node, env);
389:
390: newEnv.addVar(name, var);
391: }
392:
393: /**
394: * Sets a variable with the expression's value.
395: */
396: public void setVar(String name, Node node, Env env)
397: throws XPathException {
398: env.setVar(name, evalVar(node, env));
399: }
400:
401: /**
402: * Adds a param with the expression's value.
403: */
404: public void addParam(Env newEnv, String name, Node node, Env env)
405: throws XPathException {
406: Var var = env.getVar(name);
407:
408: if (var == null)
409: newEnv.addVar(name, evalVar(node, env));
410: else
411: newEnv.addVar(name, var);
412: }
413:
414: /**
415: * Convert a Java object to a boolean using the XPath rules.
416: */
417: public static boolean toBoolean(Object value) throws XPathException {
418: if (value instanceof Node)
419: value = XmlUtil.textValue((Node) value);
420: else if (value instanceof NodeList) {
421: NodeList list = (NodeList) value;
422:
423: return list.item(0) != null;
424: } else if (value instanceof ArrayList) {
425: ArrayList list = (ArrayList) value;
426: return list.size() > 0;
427: } else if (value instanceof Iterator) {
428: return ((Iterator) value).hasNext();
429: }
430:
431: if (value == null)
432: return false;
433:
434: else if (value instanceof Double) {
435: Double d = (Double) value;
436:
437: return d.doubleValue() != 0;
438: } else if (value instanceof Boolean) {
439: Boolean b = (Boolean) value;
440: return b.booleanValue();
441: } else if (value instanceof String) {
442: String string = (String) value;
443: return string != null && string.length() > 0;
444: } else
445: return true;
446: }
447:
448: /**
449: * Convert a Java object to a double using the XPath rules.
450: */
451: public static double toDouble(Object value) throws XPathException {
452: if (value instanceof Node) {
453: String string = XmlUtil.textValue((Node) value);
454:
455: if (string == null)
456: return 0;
457: else
458: return stringToNumber(string);
459: } else if (value instanceof NodeList) {
460: NodeList list = (NodeList) value;
461:
462: value = list.item(0);
463: } else if (value instanceof ArrayList) {
464: ArrayList list = (ArrayList) value;
465: if (list.size() > 0)
466: value = list.get(0);
467: else
468: value = null;
469: } else if (value instanceof NodeIterator) {
470: value = ((NodeIterator) value).nextNode();
471: }
472:
473: if (value instanceof Node)
474: value = XmlUtil.textValue((Node) value);
475:
476: if (value == null)
477: return 0;
478:
479: if (value instanceof Number) {
480: Number d = (Number) value;
481:
482: return d.doubleValue();
483: } else if (value instanceof Boolean) {
484: Boolean b = (Boolean) value;
485: return b.booleanValue() ? 1 : 0;
486: } else if (value instanceof String) {
487: return stringToNumber((String) value);
488: } else
489: return 0;
490: }
491:
492: /**
493: * Convert a Java object to a string using the XPath rules.
494: */
495: public static String toString(Object value) throws XPathException {
496: if (value instanceof Node) {
497: String s = XmlUtil.textValue((Node) value);
498:
499: if (s == null)
500: return "";
501: else
502: return s;
503: } else if (value instanceof NodeList) {
504: NodeList list = (NodeList) value;
505:
506: value = list.item(0);
507: } else if (value instanceof ArrayList) {
508: ArrayList list = (ArrayList) value;
509: if (list.size() > 0)
510: value = list.get(0);
511: else
512: value = null;
513: } else if (value instanceof Iterator) {
514: value = ((Iterator) value).next();
515: }
516:
517: if (value instanceof Node)
518: value = XmlUtil.textValue((Node) value);
519: else if (value instanceof Double) {
520: double d = ((Double) value).doubleValue();
521:
522: if ((int) d == d)
523: return String.valueOf((int) d);
524: else
525: return String.valueOf(d);
526: }
527:
528: if (value == null)
529: return "";
530: else
531: return value.toString();
532: }
533:
534: /**
535: * Convert a Java object to a node using the XPath rules.
536: */
537: public static Node toNode(Object value) throws XPathException {
538: if (value instanceof Node)
539: return (Node) value;
540: else if (value instanceof NodeList) {
541: NodeList list = (NodeList) value;
542: value = list.item(0);
543: } else if (value instanceof ArrayList) {
544: ArrayList list = (ArrayList) value;
545: if (list.size() > 0)
546: value = list.get(0);
547: else
548: value = null;
549: } else if (value instanceof NodeIterator) {
550: value = ((NodeIterator) value).nextNode();
551: }
552:
553: if (value instanceof Node)
554: return (Node) value;
555: else
556: return null;
557: }
558:
559: /**
560: * Convert a string to a double following XPath.
561: *
562: * @param string string to be treated as a double.
563: * @return the double value.
564: */
565: static protected double stringToNumber(String string)
566: throws XPathException {
567: int i = 0;
568: int length = string.length();
569: boolean isNumber = false;
570: for (; i < length && XmlChar.isWhitespace(string.charAt(i)); i++) {
571: }
572:
573: if (i >= length)
574: return 0;
575:
576: int ch = string.charAt(i);
577: ;
578: int sign = 1;
579: if (ch == '-') {
580: sign = -1;
581: for (i++; i < length
582: && XmlChar.isWhitespace(string.charAt(i)); i++) {
583: }
584: }
585:
586: double value = 0;
587: double exp = 1;
588: for (; i < length && (ch = string.charAt(i)) >= '0'
589: && ch <= '9'; i++) {
590: value = 10 * value + ch - '0';
591: isNumber = true;
592: }
593:
594: if (ch == '.') {
595: for (i++; i < length && (ch = string.charAt(i)) >= '0'
596: && ch <= '9'; i++) {
597: value = 10 * value + ch - '0';
598: isNumber = true;
599: exp = 10 * exp;
600: }
601: }
602:
603: double pexp = 1.0;
604: if (ch == 'e' || ch == 'E') {
605: int eSign = 1;
606: i++;
607:
608: if (i >= length)
609: return Double.NaN;
610:
611: if (string.charAt(i) == '-') {
612: eSign = -1;
613: i++;
614: } else if (string.charAt(i) == '+') {
615: i++;
616: }
617:
618: int v = 0;
619: for (; i < length && (ch = string.charAt(i)) >= '0'
620: && ch <= '9'; i++) {
621: v = v * 10 + ch - '0';
622: }
623:
624: pexp = Math.pow(10, eSign * v);
625: }
626:
627: for (; i < length && XmlChar.isWhitespace(string.charAt(i)); i++) {
628: }
629:
630: if (i < length || !isNumber)
631: return Double.NaN;
632: else
633: return sign * value * pexp / exp;
634: }
635:
636: /**
637: * Convert from an expression to a pattern.
638: */
639: protected AbstractPattern toNodeList() {
640: return new FromExpr(null, this);
641: }
642: }
|