001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.kernel.exps;
020:
021: import java.util.ArrayList;
022: import java.util.Arrays;
023: import java.util.Collection;
024: import java.util.Collections;
025: import java.util.Comparator;
026: import java.util.HashSet;
027: import java.util.Iterator;
028: import java.util.List;
029: import java.util.ListIterator;
030: import java.util.Set;
031:
032: import org.apache.commons.lang.ObjectUtils;
033: import org.apache.openjpa.kernel.Extent;
034: import org.apache.openjpa.kernel.StoreContext;
035: import org.apache.openjpa.lib.util.Localizer;
036: import org.apache.openjpa.meta.ClassMetaData;
037: import org.apache.openjpa.util.ImplHelper;
038: import org.apache.openjpa.util.UserException;
039:
040: /**
041: * Expression factory implementation that can be used to execute queries
042: * in memory.
043: *
044: * @author Abe White
045: * @nojavadoc
046: */
047: public class InMemoryExpressionFactory implements ExpressionFactory {
048:
049: private static final Value NULL = new Null();
050: private static final Value CURRENT_DATE = new CurrentDate();
051: private static final Object UNIQUE = new Object();
052:
053: // list of unbound variables in this query
054: private List _unbounds = null;
055:
056: /**
057: * Tests whether the given candidate matches the given type and this
058: * expression.
059: */
060: public boolean matches(QueryExpressions exps, ClassMetaData type,
061: boolean subs, Object candidate, StoreContext ctx,
062: Object[] params) {
063: // ignore candidates of the wrong type
064: if (candidate == null)
065: return false;
066: if (!subs && candidate.getClass() != type.getDescribedType())
067: return false;
068: if (subs
069: && !type.getDescribedType().isAssignableFrom(
070: candidate.getClass()))
071: return false;
072:
073: // evaluate the expression for all possible combinations of values
074: // of the unbound variables; the candidate matches if any combination
075: // matches
076: return matches((Exp) exps.filter, candidate, ctx, params, 0);
077: }
078:
079: /**
080: * Recursive method to evaluate the expression for all possible
081: * combinations of unbound variables. This method simulates a sequence
082: * of embedded procedural loops over the extents of all variables in the
083: * unbounds list.
084: */
085: private boolean matches(Exp exp, Object candidate,
086: StoreContext ctx, Object[] params, int i) {
087: // base case: all variables have been aliased; evaluate for current
088: // values
089: if (_unbounds == null || i == _unbounds.size())
090: return exp.evaluate(candidate, candidate, ctx, params);
091:
092: // grab the extent for this variable
093: UnboundVariable var = (UnboundVariable) _unbounds.get(i);
094: Iterator itr = ctx.extentIterator(var.getType(), true, null,
095: false);
096: try {
097: // if the extent was empty, then alias the variable to null
098: if (!itr.hasNext()) {
099: var.setValue(null);
100: return matches(exp, candidate, ctx, params, i + 1);
101: }
102:
103: // try every value, short-circuiting on match
104: while (itr.hasNext()) {
105: // set the variable to each extent value and recurse
106: var.setValue(itr.next());
107: if (matches(exp, candidate, ctx, params, i + 1))
108: return true;
109: }
110:
111: // no match
112: return false;
113: } finally {
114: ImplHelper.close(itr);
115: }
116: }
117:
118: /**
119: * Group the list of matches into a list of lists.
120: */
121: public List group(QueryExpressions exps, List matches,
122: StoreContext ctx, Object[] params) {
123: if (matches == null || matches.isEmpty()
124: || exps.grouping.length == 0)
125: return matches;
126:
127: // to form groups we first order on the grouping criteria
128: matches = order(exps, exps.grouping, false, matches, ctx,
129: params);
130:
131: // now we combine all results whose values for each grouping clause
132: // are the same, relying on the fact that these values will already be
133: // together due to the sorting
134: Object[] prevs = new Object[exps.grouping.length];
135: Arrays.fill(prevs, UNIQUE);
136: Object[] curs = new Object[exps.grouping.length];
137: List grouped = new ArrayList();
138: List group = null;
139: Object pc;
140: boolean eq;
141: for (Iterator itr = matches.iterator(); itr.hasNext();) {
142: pc = itr.next();
143: eq = true;
144: for (int i = 0; i < exps.grouping.length; i++) {
145: curs[i] = ((Val) exps.grouping[i]).evaluate(pc, pc,
146: ctx, params);
147: eq = eq && ObjectUtils.equals(prevs[i], curs[i]);
148: }
149:
150: // if this object's grouping values differ from the prev,
151: // start a new group
152: if (!eq) {
153: if (group != null)
154: grouped.add(group);
155: group = new ArrayList();
156: }
157: group.add(pc);
158: System.arraycopy(curs, 0, prevs, 0, curs.length);
159: }
160: // add the last group formed
161: if (group != null)
162: grouped.add(group);
163:
164: return grouped;
165: }
166:
167: /**
168: * Return true if the given group matches the having expression.
169: */
170: public boolean matches(QueryExpressions exps, Collection group,
171: StoreContext ctx, Object[] params) {
172: if (group == null || group.isEmpty())
173: return false;
174: if (exps.having == null)
175: return true;
176:
177: // evaluate the expression for all possible combinations of values
178: // of the unbound variables; the group matches if any combination
179: // matches
180: return matches((Exp) exps.having, group, ctx, params, 0);
181: }
182:
183: /**
184: * Recursive method to evaluate the expression for all possible
185: * combinations of unbound variables. This method simulates a sequence
186: * of embedded procedural loops over the extents of all variables in the
187: * unbounds list.
188: */
189: private boolean matches(Exp exp, Collection group,
190: StoreContext ctx, Object[] params, int i) {
191: // base case: all variables have been aliased; evaluate for current
192: // values
193: if (_unbounds == null || i == _unbounds.size())
194: return exp.evaluate(group, ctx, params);
195:
196: // grab the extent for this variable
197: UnboundVariable var = (UnboundVariable) _unbounds.get(i);
198: Extent extent = ctx.getBroker().newExtent(var.getType(), true);
199: Iterator itr = extent.iterator();
200: try {
201: // if the extent was empty, then alias the variable to null
202: if (!itr.hasNext()) {
203: var.setValue(null);
204: return matches(exp, group, ctx, params, i + 1);
205: }
206:
207: // try every value, short-circuiting on match
208: while (itr.hasNext()) {
209: // set the variable to each extent value and recurse
210: var.setValue(itr.next());
211: if (matches(exp, group, ctx, params, i + 1))
212: return true;
213: }
214:
215: // no match
216: return false;
217: } finally {
218: ImplHelper.close(itr);
219: }
220: }
221:
222: /**
223: * Create the projections for the given results.
224: */
225: public List project(QueryExpressions exps, List matches,
226: StoreContext ctx, Object[] params) {
227: if (exps.projections.length == 0)
228: return matches;
229:
230: // if an ungrouped aggregate, evaluate the whole matches list
231: if (exps.grouping.length == 0 && exps.isAggregate()) {
232: Object[] projection = project(matches, exps, true, ctx,
233: params);
234: return Arrays.asList(new Object[] { projection });
235: }
236:
237: // evaluate each candidate
238: List projected = new ArrayList(matches.size());
239: for (Iterator itr = matches.iterator(); itr.hasNext();)
240: projected.add(project(itr.next(), exps,
241: exps.grouping.length > 0, ctx, params));
242: return projected;
243: }
244:
245: /**
246: * Generate a projection on the given candidate.
247: */
248: private Object[] project(Object candidate, QueryExpressions exps,
249: boolean agg, StoreContext ctx, Object[] params) {
250: Object[] projection = new Object[exps.projections.length
251: + exps.ordering.length];
252:
253: // calcualte result values
254: Object result = null;
255: for (int i = 0; i < exps.projections.length; i++) {
256: if (agg)
257: result = ((Val) exps.projections[i]).evaluate(
258: (Collection) candidate, null, ctx, params);
259: else
260: result = ((Val) exps.projections[i]).evaluate(
261: candidate, candidate, ctx, params);
262: projection[i] = result;
263: }
264:
265: // tack on ordering values
266: boolean repeat;
267: for (int i = 0; i < exps.ordering.length; i++) {
268: // already selected as a result?
269: repeat = false;
270: for (int j = 0; !repeat && j < exps.projections.length; j++) {
271: if (exps.orderingClauses[i]
272: .equals(exps.projectionClauses[j])) {
273: result = projection[j];
274: repeat = true;
275: }
276: }
277:
278: // not selected as result; calculate value
279: if (!repeat) {
280: if (agg)
281: result = ((Val) exps.ordering[i]).evaluate(
282: (Collection) candidate, null, ctx, params);
283: else
284: result = ((Val) exps.ordering[i]).evaluate(
285: candidate, candidate, ctx, params);
286: }
287:
288: projection[i + exps.projections.length] = result;
289: }
290: return projection;
291: }
292:
293: /**
294: * Order the given list of matches on the given value.
295: */
296: public List order(QueryExpressions exps, List matches,
297: StoreContext ctx, Object[] params) {
298: return order(exps, exps.ordering, true, matches, ctx, params);
299: }
300:
301: /**
302: * Order the given list of matches on the given value.
303: *
304: * @param projected whether projections have been applied to the matches yet
305: */
306: private List order(QueryExpressions exps, Value[] orderValues,
307: boolean projected, List matches, StoreContext ctx,
308: Object[] params) {
309: if (matches == null || matches.isEmpty() || orderValues == null
310: || orderValues.length == 0)
311: return matches;
312:
313: int results = (projected) ? exps.projections.length : 0;
314: boolean[] asc = (projected) ? exps.ascending : null;
315: int idx;
316: for (int i = orderValues.length - 1; i >= 0; i--) {
317: // if this is a projection, then in project() we must have selected
318: // the ordering value already after the projection values
319: idx = (results > 0) ? results + i : -1;
320: Collections.sort(matches, new OrderValueComparator(
321: (Val) orderValues[i], asc == null || asc[i], idx,
322: ctx, params));
323: }
324: return matches;
325: }
326:
327: /**
328: * Filter the given list of matches, removing duplicate entries.
329: */
330: public List distinct(QueryExpressions exps, boolean fromExtent,
331: List matches) {
332: if (matches == null || matches.isEmpty())
333: return matches;
334:
335: // no need to do distinct if not instructed to, or if these are
336: // candidate objects from an extent
337: int len = exps.projections.length;
338: if ((exps.distinct & exps.DISTINCT_TRUE) == 0
339: || (fromExtent && len == 0))
340: return matches;
341:
342: Set seen = new HashSet(matches.size());
343: List distinct = null;
344: Object cur;
345: Object key;
346: for (ListIterator li = matches.listIterator(); li.hasNext();) {
347: cur = li.next();
348: key = (len > 0 && cur != null) ? new ArrayKey(
349: (Object[]) cur) : cur;
350:
351: if (seen.add(key)) {
352: // key hasn't been seen before; if we've created a distinct
353: // list, keep adding to it
354: if (distinct != null)
355: distinct.add(cur);
356: } else if (distinct == null) {
357: // we need to copy the matches list because the distinct list
358: // will be different (we've come across a non-unique key); add
359: // all the elements we've skipped over so far
360: distinct = new ArrayList(matches.size());
361: distinct.addAll(matches.subList(0, li.previousIndex()));
362: }
363: }
364: return (distinct == null) ? matches : distinct;
365: }
366:
367: public Expression emptyExpression() {
368: return new Exp();
369: }
370:
371: public Expression asExpression(Value v) {
372: return new ValExpression((Val) v);
373: }
374:
375: public Expression equal(Value v1, Value v2) {
376: return new EqualExpression((Val) v1, (Val) v2);
377: }
378:
379: public Expression notEqual(Value v1, Value v2) {
380: return new NotEqualExpression((Val) v1, (Val) v2);
381: }
382:
383: public Expression lessThan(Value v1, Value v2) {
384: return new LessThanExpression((Val) v1, (Val) v2);
385: }
386:
387: public Expression greaterThan(Value v1, Value v2) {
388: return new GreaterThanExpression((Val) v1, (Val) v2);
389: }
390:
391: public Expression lessThanEqual(Value v1, Value v2) {
392: return new LessThanEqualExpression((Val) v1, (Val) v2);
393: }
394:
395: public Expression greaterThanEqual(Value v1, Value v2) {
396: return new GreaterThanEqualExpression((Val) v1, (Val) v2);
397: }
398:
399: public Expression isEmpty(Value v1) {
400: return new IsEmptyExpression((Val) v1);
401: }
402:
403: public Expression isNotEmpty(Value v1) {
404: return not(isEmpty(v1));
405: }
406:
407: public Expression contains(Value v1, Value v2) {
408: return new ContainsExpression((Val) v1, (Val) v2);
409: }
410:
411: public Expression containsKey(Value v1, Value v2) {
412: return new ContainsKeyExpression((Val) v1, (Val) v2);
413: }
414:
415: public Expression containsValue(Value v1, Value v2) {
416: return new ContainsValueExpression((Val) v1, (Val) v2);
417: }
418:
419: public Value getMapValue(Value map, Value arg) {
420: return new GetMapValue((Val) map, (Val) arg);
421: }
422:
423: public Expression isInstance(Value v1, Class c) {
424: return new InstanceofExpression((Val) v1, c);
425: }
426:
427: public Expression and(Expression exp1, Expression exp2) {
428: if (exp1 instanceof BindVariableExpression)
429: return new BindVariableAndExpression(
430: (BindVariableExpression) exp1, (Exp) exp2);
431: return new AndExpression((Exp) exp1, (Exp) exp2);
432: }
433:
434: public Expression or(Expression exp1, Expression exp2) {
435: return new OrExpression((Exp) exp1, (Exp) exp2);
436: }
437:
438: public Expression not(Expression exp) {
439: return new NotExpression((Exp) exp);
440: }
441:
442: public Expression bindVariable(Value var, Value val) {
443: return new BindVariableExpression((BoundVariable) var,
444: (Val) val);
445: }
446:
447: public Expression bindKeyVariable(Value var, Value val) {
448: return new BindKeyVariableExpression((BoundVariable) var,
449: (Val) val);
450: }
451:
452: public Expression bindValueVariable(Value var, Value val) {
453: return new BindValueVariableExpression((BoundVariable) var,
454: (Val) val);
455: }
456:
457: public Expression endsWith(Value v1, Value v2) {
458: return new EndsWithExpression((Val) v1, (Val) v2);
459: }
460:
461: public Expression matches(Value v1, Value v2, String single,
462: String multi, String esc) {
463: return new MatchesExpression((Val) v1, (Val) v2, single, multi,
464: esc, true);
465: }
466:
467: public Expression notMatches(Value v1, Value v2, String single,
468: String multi, String esc) {
469: return new MatchesExpression((Val) v1, (Val) v2, single, multi,
470: esc, false);
471: }
472:
473: public Expression startsWith(Value v1, Value v2) {
474: return new StartsWithExpression((Val) v1, (Val) v2);
475: }
476:
477: public Subquery newSubquery(ClassMetaData candidate, boolean subs,
478: String alias) {
479: return new SubQ(alias);
480: }
481:
482: public Path newPath() {
483: return new CandidatePath();
484: }
485:
486: public Path newPath(Value val) {
487: return new ValuePath((Val) val);
488: }
489:
490: public Literal newLiteral(Object val, int parseType) {
491: return new Lit(val, parseType);
492: }
493:
494: public Value getThis() {
495: return new This();
496: }
497:
498: public Value getNull() {
499: return NULL;
500: }
501:
502: public Value getCurrentDate() {
503: return CURRENT_DATE;
504: }
505:
506: public Value getCurrentTime() {
507: return CURRENT_DATE;
508: }
509:
510: public Value getCurrentTimestamp() {
511: return CURRENT_DATE;
512: }
513:
514: public Parameter newParameter(String name, Class type) {
515: return new Param(name, type);
516: }
517:
518: public Value newExtension(FilterListener listener, Value target,
519: Value arg) {
520: return new Extension(listener, (Val) target, (Val) arg);
521: }
522:
523: public Value newAggregate(AggregateListener listener, Value arg) {
524: return new Aggregate(listener, (Val) arg);
525: }
526:
527: public Arguments newArgumentList(Value val1, Value val2) {
528: return new Args(val1, val2);
529: }
530:
531: public Value newUnboundVariable(String name, Class type) {
532: UnboundVariable var = new UnboundVariable(type);
533: if (_unbounds == null)
534: _unbounds = new ArrayList(3);
535: _unbounds.add(var);
536: return var;
537: }
538:
539: public Value newBoundVariable(String name, Class type) {
540: return new BoundVariable(type);
541: }
542:
543: public Value cast(Value val, Class cls) {
544: if (val instanceof CandidatePath)
545: ((CandidatePath) val).castTo(cls);
546: else if (val instanceof BoundVariable)
547: ((BoundVariable) val).castTo(cls);
548: else
549: val = new Cast((Val) val, cls);
550: return val;
551: }
552:
553: public Value add(Value val1, Value val2) {
554: return new Add((Val) val1, (Val) val2);
555: }
556:
557: public Value subtract(Value val1, Value val2) {
558: return new Subtract((Val) val1, (Val) val2);
559: }
560:
561: public Value multiply(Value val1, Value val2) {
562: return new Multiply((Val) val1, (Val) val2);
563: }
564:
565: public Value divide(Value val1, Value val2) {
566: return new Divide((Val) val1, (Val) val2);
567: }
568:
569: public Value mod(Value val1, Value val2) {
570: return new Mod((Val) val1, (Val) val2);
571: }
572:
573: public Value abs(Value val) {
574: return new Abs((Val) val);
575: }
576:
577: public Value indexOf(Value val1, Value val2) {
578: return new IndexOf((Val) val1, (Val) val2);
579: }
580:
581: public Value concat(Value val1, Value val2) {
582: return new Concat((Val) val1, (Val) val2);
583: }
584:
585: public Value stringLength(Value str) {
586: return new StringLength((Val) str);
587: }
588:
589: public Value trim(Value str, Value trimChar, Boolean where) {
590: return new Trim((Val) str, (Val) trimChar, where);
591: }
592:
593: public Value sqrt(Value val) {
594: return new Sqrt((Val) val);
595: }
596:
597: public Value substring(Value val1, Value val2) {
598: return new Substring((Val) val1, (Val) val2);
599: }
600:
601: public Value toUpperCase(Value val) {
602: return new ToUpperCase((Val) val);
603: }
604:
605: public Value toLowerCase(Value val) {
606: return new ToLowerCase((Val) val);
607: }
608:
609: public Value avg(Value val) {
610: return new Avg((Val) val);
611: }
612:
613: public Value count(Value val) {
614: return new Count((Val) val);
615: }
616:
617: public Value distinct(Value val) {
618: return new Distinct((Val) val);
619: }
620:
621: public Value max(Value val) {
622: return new Max((Val) val);
623: }
624:
625: public Value min(Value val) {
626: return new Min((Val) val);
627: }
628:
629: public Value sum(Value val) {
630: return new Sum((Val) val);
631: }
632:
633: public Value any(Value val) {
634: return new Any((Val) val);
635: }
636:
637: public Value all(Value val) {
638: return new All((Val) val);
639: }
640:
641: public Value size(Value val) {
642: return new Size((Val) val);
643: }
644:
645: public Value getObjectId(Value val) {
646: return new GetObjectId((Val) val);
647: }
648:
649: /**
650: * Key that implements hashCode and equals methods for object arrays.
651: */
652: private static class ArrayKey {
653:
654: private final Object[] _arr;
655:
656: public ArrayKey(Object[] arr) {
657: _arr = arr;
658: }
659:
660: public int hashCode() {
661: int rs = 17;
662: for (int i = 0; i < _arr.length; i++)
663: rs = 37 * rs
664: + ((_arr[i] == null) ? 0 : _arr[i].hashCode());
665: return rs;
666: }
667:
668: public boolean equals(Object other) {
669: if (other == this )
670: return true;
671: if (other == null)
672: return false;
673:
674: Object[] arr = ((ArrayKey) other)._arr;
675: if (_arr.length != arr.length)
676: return false;
677: for (int i = 0; i < _arr.length; i++)
678: if (!ObjectUtils.equals(_arr[i], arr[i]))
679: return false;
680: return true;
681: }
682: }
683:
684: /**
685: * Comparator that uses the result of eval'ing a Value to sort on. Null
686: * values are placed last if sorting in ascending order, first if
687: * descending.
688: */
689: private static class OrderValueComparator implements Comparator {
690:
691: private final StoreContext _ctx;
692: private final Val _val;
693: private final boolean _asc;
694: private final int _idx;
695: private final Object[] _params;
696:
697: private OrderValueComparator(Val val, boolean asc, int idx,
698: StoreContext ctx, Object[] params) {
699: _ctx = ctx;
700: _val = val;
701: _asc = asc;
702: _idx = idx;
703: _params = params;
704: }
705:
706: public int compare(Object o1, Object o2) {
707: if (_idx != -1) {
708: o1 = ((Object[]) o1)[_idx];
709: o2 = ((Object[]) o2)[_idx];
710: } else {
711: o1 = _val.evaluate(o1, o1, _ctx, _params);
712: o2 = _val.evaluate(o2, o2, _ctx, _params);
713: }
714:
715: if (o1 == null && o2 == null)
716: return 0;
717: if (o1 == null)
718: return (_asc) ? 1 : -1;
719: if (o2 == null)
720: return (_asc) ? -1 : 1;
721:
722: if (o1 instanceof Boolean && o2 instanceof Boolean) {
723: int i1 = ((Boolean) o1).booleanValue() ? 1 : 0;
724: int i2 = ((Boolean) o2).booleanValue() ? 1 : 0;
725: return i1 - i2;
726: }
727:
728: try {
729: if (_asc)
730: return ((Comparable) o1).compareTo(o2);
731: return ((Comparable) o2).compareTo(o1);
732: } catch (ClassCastException cce) {
733: Localizer loc = Localizer
734: .forPackage(InMemoryExpressionFactory.class);
735: throw new UserException(loc.get("not-comp", o1, o2));
736: }
737: }
738: }
739: }
|