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.expr;
030:
031: import com.caucho.xml.XmlUtil;
032: import com.caucho.xpath.Env;
033: import com.caucho.xpath.Expr;
034: import com.caucho.xpath.ExprEnvironment;
035: import com.caucho.xpath.XPathException;
036: import com.caucho.xpath.pattern.AbstractPattern;
037:
038: import org.w3c.dom.Node;
039:
040: import java.util.ArrayList;
041: import java.util.Iterator;
042:
043: public class NumericExpr extends Expr {
044: private int _code;
045: private Expr _left;
046: private Expr _right;
047: private double _value;
048: private ArrayList<Expr> _args;
049: private AbstractPattern _axis;
050: private AbstractPattern _pattern;
051:
052: public NumericExpr(int code, Expr left, Expr right) {
053: _code = code;
054: _left = left;
055: _right = right;
056: }
057:
058: public NumericExpr(int code, Expr expr) {
059: _code = code;
060: _left = expr;
061: }
062:
063: public NumericExpr(double value) {
064: _code = CONST;
065: _value = value;
066: }
067:
068: public NumericExpr(int code, ArrayList<Expr> args) {
069: _code = code;
070: _args = args;
071:
072: if (args.size() > 0)
073: _left = args.get(0);
074: if (args.size() > 1)
075: _right = args.get(1);
076: }
077:
078: public NumericExpr(int code, AbstractPattern axis,
079: AbstractPattern pattern) {
080: _code = code;
081: _axis = axis;
082: _pattern = pattern;
083: }
084:
085: public NumericExpr(int code, AbstractPattern listPattern) {
086: _code = code;
087:
088: if ((code == POSITION || code == LAST) && listPattern != null) {
089: _axis = listPattern.copyAxis();
090: _pattern = listPattern.copyPosition();
091: } else
092: _pattern = listPattern;
093: }
094:
095: public AbstractPattern getListContext() {
096: switch (_code) {
097: case POSITION:
098: case LAST:
099: return _pattern;
100:
101: default:
102: return null;
103: }
104: }
105:
106: public boolean isNumber() {
107: return true;
108: }
109:
110: /**
111: * Returns true of the expression is constant.
112: */
113: public boolean isConstant() {
114: return _code == CONST;
115: }
116:
117: /**
118: * Returns the expression's value.
119: */
120: public double getValue() {
121: return _value;
122: }
123:
124: /**
125: * Evaluates to a variable.
126: *
127: * @param node the node to evaluate and use as a context.
128: * @param env the variable environment.
129: *
130: * @return a variable containing the value.
131: */
132: public Var evalVar(Node node, ExprEnvironment env)
133: throws XPathException {
134: double value = evalNumber(node, env);
135:
136: return NumberVar.create(value);
137: }
138:
139: /**
140: * Evaluates the expression as a number.
141: *
142: * @param node the node to evaluate and use as a context.
143: * @param env the variable environment.
144: *
145: * @return the numeric value
146: */
147: public double evalNumber(Node node, ExprEnvironment env)
148: throws XPathException {
149: switch (_code) {
150: case CONST:
151: return _value;
152:
153: case NEG:
154: return -_left.evalNumber(node, env);
155:
156: case ADD:
157: return (_left.evalNumber(node, env) + _right.evalNumber(
158: node, env));
159:
160: case SUB:
161: return (_left.evalNumber(node, env) - _right.evalNumber(
162: node, env));
163:
164: case MUL:
165: return (_left.evalNumber(node, env) * _right.evalNumber(
166: node, env));
167:
168: case DIV:
169: return (_left.evalNumber(node, env) / _right.evalNumber(
170: node, env));
171:
172: case QUO:
173: return (int) (_left.evalNumber(node, env) / _right
174: .evalNumber(node, env));
175:
176: case MOD:
177: return (_left.evalNumber(node, env) % _right.evalNumber(
178: node, env));
179:
180: case NUMBER:
181: if (_left != null)
182: return _left.evalNumber(node, env);
183: else
184: return toDouble(node);
185:
186: case FLOOR:
187: return Math.floor(_left.evalNumber(node, env));
188:
189: case CEILING:
190: return Math.ceil(_left.evalNumber(node, env));
191:
192: case ROUND:
193: return Math.rint(_left.evalNumber(node, env));
194:
195: case SUM:
196: return sum(node, env);
197:
198: case POSITION:
199: return position(node, env);
200:
201: case LAST:
202: return last(node, env);
203:
204: case COUNT:
205: return count(node, env);
206:
207: case STRING_LENGTH:
208: String str = _left.evalString(node, env);
209: if (str == null)
210: return 0;
211: else
212: return str.length();
213:
214: default:
215: throw new RuntimeException("unknown code: " + (char) _code);
216: }
217: }
218:
219: /**
220: * Calculates the position of the node. For select patterns, the
221: * position will be given in the env variable.
222: */
223: private int position(Node node, ExprEnvironment env)
224: throws XPathException {
225: int position = env.getContextPosition();
226:
227: if (position > 0)
228: return position;
229:
230: if (_axis == null || !(env instanceof Env))
231: throw new RuntimeException(
232: "position called with no context");
233: else if (_pattern == null)
234: return _axis.position(node, (Env) env, null);
235: else
236: return _axis.position(node, (Env) env, _pattern
237: .copyPosition());
238: }
239:
240: private int last(Node node, ExprEnvironment env)
241: throws XPathException {
242: int size = env.getContextSize();
243:
244: if (size > 0)
245: return size;
246:
247: if (_axis == null || !(env instanceof Env)) {
248: throw new RuntimeException("last called with no context");
249: } else if (_pattern == null)
250: return _axis.position(node, (Env) env, null);
251: else
252: return _axis
253: .count(node, (Env) env, _pattern.copyPosition());
254: }
255:
256: /**
257: * Counts the nodes in a subpattern.
258: */
259: private int count(Node node, ExprEnvironment env)
260: throws XPathException {
261: int count = 0;
262:
263: Iterator iter = _pattern.selectUnique(node, env);
264: while (iter.hasNext()) {
265: iter.next();
266: count++;
267: }
268:
269: return count;
270: }
271:
272: /**
273: * Returns the sum of all the node values.
274: *
275: * @param node the current node
276: * @param env the variable environment
277: */
278: private double sum(Node node, ExprEnvironment env)
279: throws XPathException {
280: double sum = 0;
281:
282: Iterator iter = _pattern.selectUnique(node, env);
283: while (iter.hasNext()) {
284: Node subnode = (Node) iter.next();
285: String textValue = XmlUtil.textValue(subnode);
286:
287: sum += stringToNumber(textValue);
288: }
289:
290: return sum;
291: }
292:
293: /**
294: * Evaluates the expression as a boolean.
295: *
296: * @param node the current node
297: * @param env the variable environment.
298: *
299: * @return the boolean representation of the number.
300: */
301: public boolean evalBoolean(Node node, ExprEnvironment env)
302: throws XPathException {
303: double value = evalNumber(node, env);
304:
305: return value != 0.0 && !Double.isNaN(value);
306: }
307:
308: /**
309: * Evaluates the expression as a string.
310: *
311: * @param node the current node
312: * @param env the variable environment.
313: *
314: * @return the string representation of the number.
315: */
316: public String evalString(Node node, ExprEnvironment env)
317: throws XPathException {
318: double value = evalNumber(node, env);
319:
320: if ((int) value == value)
321: return String.valueOf((int) value);
322: else
323: return String.valueOf(value);
324: }
325:
326: /**
327: * Evaluates the expression as an object.
328: *
329: * @param node the current node
330: * @param env the variable environment.
331: *
332: * @return the Double representation of the number.
333: */
334: public Object evalObject(Node node, ExprEnvironment env)
335: throws XPathException {
336: return new Double(evalNumber(node, env));
337: }
338:
339: public String toString() {
340: switch (_code) {
341: case CONST:
342: return String.valueOf(_value);
343: case NEG:
344: return "-" + _left;
345: case ADD:
346: return "(" + _left + " + " + _right + ")";
347: case SUB:
348: return "(" + _left + " - " + _right + ")";
349: case MUL:
350: return "(" + _left + " * " + _right + ")";
351: case DIV:
352: return "(" + _left + " div " + _right + ")";
353: case QUO:
354: return "(" + _left + " quo " + _right + ")";
355: case MOD:
356: return "(" + _left + " mod " + _right + ")";
357:
358: case NUMBER:
359: return "number(" + _left + ")";
360: case SUM:
361: return "sum(" + _pattern + ")";
362: case FLOOR:
363: return "floor(" + _left + ")";
364: case CEILING:
365: return "ceiling(" + _left + ")";
366: case ROUND:
367: return "round(" + _left + ")";
368:
369: case POSITION:
370: return "position()";
371: case COUNT:
372: return "count(" + _pattern + ")";
373: case LAST:
374: return "last()";
375:
376: case STRING_LENGTH:
377: return "string-length(" + _left + ")";
378:
379: default:
380: return super.toString();
381: }
382: }
383: }
|