001: /*
002: * Copyright 2004-2007 Gary Bentley
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may
005: * not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: * http://www.apache.org/licenses/LICENSE-2.0
008: *
009: * Unless required by applicable law or agreed to in writing, software
010: * distributed under the License is distributed on an "AS IS" BASIS,
011: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: * See the License for the specific language governing permissions and
013: * limitations under the License.
014: */
015: package org.josql.expressions;
016:
017: import org.josql.Query;
018: import org.josql.QueryExecutionException;
019: import org.josql.QueryParseException;
020:
021: import org.josql.internal.Utilities;
022:
023: /**
024: * Represents the arithmetic expressions: *, +, /, - and %.
025: * It should be noted that ALL numbers in JoSQL are represented as <b>double</b> values, this
026: * allows for easy arithmetic operations without the fear of losing precision or casting
027: * issues.
028: */
029: public class ArithmeticExpression extends ValueExpression {
030:
031: public static final int MULTIPLY = 0;
032: public static final int ADDITION = 1;
033: public static final int SUBTRACT = 2;
034: public static final int DIVIDE = 3;
035: public static final int MODULUS = 4;
036:
037: private int type = -1;
038:
039: private ValueExpression left = null;
040: private ValueExpression right = null;
041:
042: private boolean fixedResult = false;
043:
044: /**
045: * Return the expected return type. This just returns the result of calling this
046: * method on the LHS.
047: *
048: * @param q The Query object.
049: * @return The expected return type class.
050: * @throws QueryParseException If something goes wrong in determining the return type.
051: */
052: public Class getExpectedReturnType(Query q)
053: throws QueryParseException {
054:
055: return this .left.getExpectedReturnType(q);
056:
057: }
058:
059: /**
060: * Determine whether this arithmetic expression evaluates to true.
061: * Whilst this seems a little bizarre this method is needed to allow
062: * artithmetic expressions to be used in the columns part of the SELECT.
063: * <p>
064: * Thus the following could be performed:
065: * <pre>
066: * SELECT 10 + 20
067: * FROM java.lang.Object
068: * </pre>
069: *
070: * The rules are as follows:
071: * <ul>
072: * <li>If {@link #evaluate(Object,Query)} returns <code>null</code> then <code>false</code>
073: * is returned.</li>
074: * <li>If {@link #evaluate(Object,Query)} returns a number and it is greater than <b>0</b> then
075: * <code>true</code> is returned. If it is <b>0</b> or less than <b>0</b>
076: * then <code>false</code> is returned.
077: * <li>If it is anything else then <code>true</code> is returned.
078: * </ul>
079: *
080: * @param o The object to perform the expression on.
081: * @param q The Query object.
082: * @return As according to the rules above.
083: * @throws QueryExecutionException If something goes wrong during the evaluation of the
084: * expression.
085: */
086: public boolean isTrue(Object o, Query q)
087: throws QueryExecutionException {
088:
089: o = this .evaluate(o, q);
090:
091: if (o == null) {
092:
093: return false;
094:
095: }
096:
097: if (o instanceof Number) {
098:
099: return ((Number) o).doubleValue() > 0;
100:
101: }
102:
103: return true;
104:
105: }
106:
107: /**
108: * Return whether this expression has a fixed result.
109: *
110: * @param q The Query object.
111: * @return {@link Expression#hasFixedResult(Query) LHS.hasFixedResult(Query)}
112: * &&
113: * {@link Expression#hasFixedResult(Query) RHS.hasFixedResult(Query)}
114: */
115: public boolean hasFixedResult(Query q) {
116:
117: return this .fixedResult;
118:
119: }
120:
121: public void init(Query q) throws QueryParseException {
122:
123: this .left.init(q);
124: this .right.init(q);
125:
126: this .fixedResult = this .left.hasFixedResult(q)
127: && this .right.hasFixedResult(q);
128:
129: }
130:
131: /**
132: * Get the RHS value expression.
133: *
134: * @return The RHS.
135: */
136: public ValueExpression getRight() {
137:
138: return this .right;
139:
140: }
141:
142: /**
143: * Get the LHS value expression.
144: *
145: * @return The LHS.
146: */
147: public ValueExpression getLeft() {
148:
149: return this .left;
150:
151: }
152:
153: public void setLeft(ValueExpression exp) {
154:
155: this .left = exp;
156:
157: }
158:
159: public void setRight(ValueExpression exp) {
160:
161: this .right = exp;
162:
163: }
164:
165: public int getType() {
166:
167: return this .type;
168:
169: }
170:
171: public void setType(int t) {
172:
173: this .type = t;
174:
175: }
176:
177: /**
178: * Evaulate this expression. Apart from the special cases the LHS and RHS must evaluate
179: * to an instance of <code>java.lang.Number</code> otherwise a QueryExecutionException is
180: * thrown.
181: * <p>
182: * Special cases:
183: * <ul>
184: * <li>If the type of the expression is + and either LHS or RHS are NOT numbers
185: * then the String concatentation of them both are returned.
186: * This works the same as in Java.</li>
187: * <li>If the type of the expression is / and the RHS is 0 then 0 is returned.</li>
188: * </ul>
189: *
190: * @param o The object to perform the expression on.
191: * @param q The Query object.
192: * @return The result of the expression, see the "special cases" for the exceptions to what would
193: * be the intuitive result.
194: * @throws QueryExecutionException If an error occurs during processing.
195: */
196: public Object evaluate(Object o, Query q)
197: throws QueryExecutionException {
198:
199: Object l = this .left.getValue(o, q);
200:
201: Object r = this .right.getValue(o, q);
202:
203: // Special case for addition.
204: if ((this .type == ArithmeticExpression.ADDITION)
205: && (!(l instanceof Number) || !(r instanceof Number))) {
206:
207: StringBuffer b = new StringBuffer();
208:
209: if (l == null) {
210:
211: b.append("null");
212:
213: } else {
214:
215: b.append(l);
216:
217: }
218:
219: if (r == null) {
220:
221: b.append("null");
222:
223: } else {
224:
225: b.append(r);
226:
227: }
228:
229: return b.toString();
230:
231: }
232:
233: if (l == null) {
234:
235: l = new Double(0);
236:
237: }
238:
239: if (r == null) {
240:
241: r = new Double(0);
242:
243: }
244:
245: double ld = Utilities.getDouble(l);
246: double rd = Utilities.getDouble(r);
247:
248: if (this .type == ArithmeticExpression.ADDITION) {
249:
250: return new Double(ld + rd);
251:
252: }
253:
254: if (this .type == ArithmeticExpression.SUBTRACT) {
255:
256: return new Double(ld - rd);
257:
258: }
259:
260: if (this .type == ArithmeticExpression.MULTIPLY) {
261:
262: return new Double(ld * rd);
263:
264: }
265:
266: if (this .type == ArithmeticExpression.MODULUS) {
267:
268: return new Double(ld % rd);
269:
270: }
271:
272: if (this .type == ArithmeticExpression.DIVIDE) {
273:
274: if (rd == 0) {
275:
276: return new Double(0);
277:
278: }
279:
280: return new Double(ld / rd);
281:
282: }
283:
284: return null;
285:
286: }
287:
288: public String toString() {
289:
290: String pred = "+";
291:
292: if (this .type == ArithmeticExpression.MULTIPLY) {
293:
294: pred = "*";
295:
296: }
297:
298: if (this .type == ArithmeticExpression.SUBTRACT) {
299:
300: pred = "-";
301:
302: }
303:
304: if (this .type == ArithmeticExpression.MODULUS) {
305:
306: pred = "%";
307:
308: }
309:
310: if (this .type == ArithmeticExpression.DIVIDE) {
311:
312: pred = "/";
313:
314: }
315:
316: String exp = this .left.toString() + " " + pred + " "
317: + this .right.toString();
318:
319: if (this .isBracketed()) {
320:
321: exp = "(" + exp + ")";
322:
323: }
324:
325: return exp;
326:
327: }
328:
329: }
|