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 java.util.List;
018: import java.util.ArrayList;
019: import java.util.Collection;
020: import java.util.Map;
021: import java.util.Iterator;
022:
023: import org.josql.Query;
024: import org.josql.QueryExecutionException;
025: import org.josql.QueryParseException;
026:
027: import org.josql.internal.Utilities;
028:
029: /**
030: * This class represents in [ NOT ] IN [ LIKE ] [ ALL ] expression.
031: * If any of the values listed are Maps or Collections then they are iterated over to see if a match
032: * is found. For Maps only the key values are checked. If you pass a list then it is iterated over
033: * using a <code>for</code> construct rather than an Iterator since it's much faster.
034: * <p>
035: * Note: due to the way that the expression is designed it is POSSIBLE to have a binary expression
036: * in the in list, however at this time that is not supported since it can lead to an ambiguous result,
037: * for example: <code>true IN (true, false)</code> has no sensible meaning.
038: */
039: public class InExpression extends BinaryExpression {
040:
041: private List items = new ArrayList();
042: private boolean not = false;
043: private boolean doLike = false;
044: private boolean all = false;
045: private boolean ignoreCase = false;
046:
047: /**
048: * Initialise the IN expression. Init the LHS and then all of the values in the brackets.
049: *
050: * @param q The Query object.
051: * @throws QueryParseException If something goes wrong with the init.
052: */
053: public void init(Query q) throws QueryParseException {
054:
055: this .left.init(q);
056:
057: int s = this .items.size();
058:
059: for (int i = 0; i < s; i++) {
060:
061: Expression exp = (Expression) this .items.get(i);
062:
063: exp.init(q);
064:
065: }
066:
067: }
068:
069: public void setIgnoreCase(boolean v) {
070:
071: this .ignoreCase = v;
072:
073: }
074:
075: public boolean isIgnoreCase() {
076:
077: return this .ignoreCase;
078:
079: }
080:
081: public boolean isAll() {
082:
083: return this .all;
084:
085: }
086:
087: public void setAll(boolean v) {
088:
089: this .all = v;
090:
091: }
092:
093: public boolean isDoLike() {
094:
095: return this .doLike;
096:
097: }
098:
099: public void setDoLike(boolean d) {
100:
101: this .doLike = d;
102:
103: }
104:
105: public void setItems(List items) {
106:
107: this .items = items;
108:
109: }
110:
111: public List getItems() {
112:
113: return this .items;
114:
115: }
116:
117: public void addItem(Expression e) {
118:
119: this .items.add(e);
120:
121: }
122:
123: public boolean isNot() {
124:
125: return this .not;
126:
127: }
128:
129: public void setNot(boolean v) {
130:
131: this .not = v;
132:
133: }
134:
135: /**
136: * Return whether this expression evaulates to <code>true</code>. If any of the values on RHS are Maps (keys
137: * only) or Collections then they are iterated over and checked against the LHS.
138: *
139: * @param o The current object to perform the expression on.
140: * @param q The Query object.
141: * @return <code>true</code> if the LHS is "equal" (as determined by: {@link Utilities#isEquals(Object,Object)})
142: * to any of the values in the brackets. If this is a NOT expression then the result is negated.
143: * @throws QueryExecutionException If the expression cannot be evaluated.
144: */
145: public boolean isTrue(Object o, Query q)
146: throws QueryExecutionException {
147:
148: // Get the LHS.
149: Object l = this .left.getValue(o, q);
150:
151: String v = null;
152: String wc = String.valueOf(q.getWildcardCharacter());
153:
154: if (this .doLike) {
155:
156: if (l != null) {
157:
158: v = l.toString();
159:
160: if (this .ignoreCase) {
161:
162: v = v.toLowerCase();
163:
164: }
165:
166: }
167:
168: }
169:
170: int count = 0;
171:
172: // Cycle over our items.
173: int s = this .items.size();
174:
175: for (int i = 0; i < s; i++) {
176:
177: Expression exp = (Expression) this .items.get(i);
178:
179: // Evaluate it.
180: Object eo = exp.getValue(o, q);
181:
182: boolean eq = false;
183: boolean proc = false;
184:
185: if (eo instanceof Collection) {
186:
187: Collection col = (Collection) eo;
188:
189: eq = this .compareCollection(l, col, v, wc);
190: proc = true;
191:
192: }
193:
194: if (eo instanceof Map) {
195:
196: eq = this .compareMap(l, (Map) eo, v, wc);
197: proc = true;
198:
199: }
200:
201: if (!proc) {
202:
203: // See if the objects are equivalent.
204: eq = this .compareItem(l, eo, v, wc);
205:
206: }
207:
208: if (eq) {
209:
210: count++;
211:
212: if (this .not) {
213:
214: return false;
215:
216: }
217:
218: if (!this .all) {
219:
220: return true;
221:
222: }
223:
224: }
225:
226: }
227:
228: if ((this .all) && (!this .not) && (count == s)) {
229:
230: return true;
231:
232: }
233:
234: if ((this .all) && (this .not) && (count == 0)) {
235:
236: return true;
237:
238: }
239:
240: if (this .not) {
241:
242: return true;
243:
244: }
245:
246: return false;
247:
248: }
249:
250: private boolean compareCollection(Object o, Collection c, String v,
251: String wc) {
252:
253: if (c instanceof List) {
254:
255: return this .compareList(o, (List) c, v, wc);
256:
257: }
258:
259: Iterator i = c.iterator();
260:
261: int count = 0;
262:
263: while (i.hasNext()) {
264:
265: Object n = i.next();
266:
267: if (this .compareItem(o, n, v, wc)) {
268:
269: count++;
270:
271: if (!this .all) {
272:
273: return true;
274:
275: }
276:
277: }
278:
279: }
280:
281: if ((this .all) && (!this .not) && (count == c.size())) {
282:
283: return true;
284:
285: }
286:
287: if ((this .all) && (this .not) && (count == 0)) {
288:
289: return true;
290:
291: }
292:
293: return false;
294:
295: }
296:
297: private boolean compareList(Object o, List l, String v, String wc) {
298:
299: int s = l.size();
300:
301: int count = 0;
302:
303: for (int i = 0; i < s; i++) {
304:
305: Object n = l.get(i);
306:
307: if (this .compareItem(o, n, v, wc)) {
308:
309: count++;
310:
311: if (!this .all) {
312:
313: return true;
314:
315: }
316:
317: }
318:
319: }
320:
321: if ((this .all) && (!this .not) && (count == s)) {
322:
323: return true;
324:
325: }
326:
327: if ((this .all) && (this .not) && (count == 0)) {
328:
329: return true;
330:
331: }
332:
333: return false;
334:
335: }
336:
337: private boolean compareItem(Object o, Object n, String v, String wc) {
338:
339: boolean eq = true;
340:
341: if (this .doLike) {
342:
343: if ((v == null) && (n == null)) {
344:
345: return true;
346:
347: }
348:
349: if (n == null) {
350:
351: return false;
352:
353: }
354:
355: String vn = n.toString();
356:
357: if (this .ignoreCase) {
358:
359: vn = vn.toLowerCase();
360:
361: }
362:
363: List pat = Utilities.getLikePattern(vn, wc);
364:
365: eq = Utilities.matchLikePattern(pat, v);
366:
367: } else {
368:
369: if (this .ignoreCase) {
370:
371: if (o == null) {
372:
373: return (n == null);
374:
375: } else {
376:
377: if (n == null) {
378:
379: return false;
380:
381: }
382:
383: }
384:
385: return o.toString().equalsIgnoreCase(n.toString());
386:
387: }
388:
389: eq = Utilities.isEquals(o, n);
390:
391: }
392:
393: return eq;
394:
395: }
396:
397: private boolean compareMap(Object o, Map m, String v, String wc) {
398:
399: Iterator i = m.keySet().iterator();
400:
401: int count = 0;
402:
403: while (i.hasNext()) {
404:
405: Object n = i.next();
406:
407: if (this .compareItem(o, n, v, wc)) {
408:
409: count++;
410:
411: if (!this .all) {
412:
413: return true;
414:
415: }
416:
417: }
418:
419: }
420:
421: if ((this .all) && (!this .not) && (count == m.size())) {
422:
423: return true;
424:
425: }
426:
427: if ((this .all) && (this .not) && (count == 0)) {
428:
429: return true;
430:
431: }
432:
433: return false;
434:
435: }
436:
437: /**
438: * Return a string representation of this expression.
439: * In the form: {@link Expression#toString() Expression} [ NOT ] [$]IN [ LIKE ] [ ALL ]
440: * ( {@link Expression#toString() Expression} [ , {@link Expression#toString() Expression}* ] )
441: *
442: * @return A string representation of this expression.
443: */
444: public String toString() {
445:
446: StringBuffer buf = new StringBuffer(this .left.toString());
447:
448: buf.append(" ");
449:
450: if (this .isNot()) {
451:
452: buf.append("NOT ");
453:
454: }
455:
456: if (this .ignoreCase) {
457:
458: buf.append("$");
459:
460: }
461:
462: buf.append("IN ");
463:
464: if (this .doLike) {
465:
466: buf.append("LIKE ");
467:
468: }
469:
470: if (this .all) {
471:
472: buf.append("ALL ");
473:
474: }
475:
476: buf.append("(");
477:
478: for (int i = 0; i < this .items.size(); i++) {
479:
480: buf.append(this .items.get(i));
481:
482: if (i < (this .items.size() - 1)) {
483:
484: buf.append(",");
485:
486: }
487:
488: }
489:
490: buf.append(")");
491:
492: if (this .isBracketed()) {
493:
494: buf.insert(0, "(");
495: buf.append(")");
496:
497: }
498:
499: return buf.toString();
500:
501: }
502:
503: }
|