protected ExpQuery( Type resultType, VarDeclList elemVarDecls, Expression rangeExp, Expression queryExp) throws ExpInvalidException { super(resultType, rangeExp, queryExp); fElemVarDecls = elemVarDecls; fRangeExp = rangeExp; fQueryExp = queryExp; // type of rangeExp must be a subtype of Collection, i.e. Set, // Sequence or Bag if (!fRangeExp.type().isCollection(false)) throw new ExpInvalidException( "Range expression must be of type " + "`Collection', found `" + fRangeExp.type() + "'."); // assert that declard element variables are equal or // supertypes of range element type Type rangeElemType = ((CollectionType) fRangeExp.type()).elemType(); for (int i = 0; i < fElemVarDecls.size(); i++) { VarDecl vd = fElemVarDecls.varDecl(i); if (!rangeElemType.isSubtypeOf(vd.type())) throw new ExpInvalidException( "Type `" + vd.type() + "' of range variable `" + vd.name() + "' does not match type `" + rangeElemType + "' of collection elements."); } }
/** Evaluate isUnique expression. */ protected final Value evalIsUnique(EvalContext ctx) { // evaluate range Value v = fRangeExp.eval(ctx); if (v.isUndefined()) return UndefinedValue.instance; CollectionValue rangeVal = (CollectionValue) v; // collect values for finding duplicates Set<Value> values = new HashSet<Value>(); // loop over range elements for (Value elemVal : rangeVal) { // bind element variable to range element, if variable was // declared if (!fElemVarDecls.isEmpty()) ctx.pushVarBinding(fElemVarDecls.varDecl(0).name(), elemVal); // evaluate collect expression Value val = fQueryExp.eval(ctx); if (!fElemVarDecls.isEmpty()) ctx.popVarBinding(); // stop if duplicate element is found if (values.contains(val)) return BooleanValue.FALSE; else values.add(val); } // result is true if no duplicates where found return BooleanValue.TRUE; }
/** Evaluate collect expressions. */ protected final Value evalCollectNested(EvalContext ctx) { // evaluate range Value v = fRangeExp.eval(ctx); if (v.isUndefined()) return UndefinedValue.instance; CollectionValue rangeVal = (CollectionValue) v; // prepare result value ArrayList<Value> resValues = new ArrayList<Value>(); // loop over range elements for (Value elemVal : rangeVal) { // bind element variable to range element, if variable was // declared if (!fElemVarDecls.isEmpty()) ctx.pushVarBinding(fElemVarDecls.varDecl(0).name(), elemVal); // evaluate collect expression Value val = fQueryExp.eval(ctx); // add element to result resValues.add(val); if (!fElemVarDecls.isEmpty()) ctx.popVarBinding(); } // result is collection with mapped values if (fRangeExp.type().isSequence() || fRangeExp.type().isOrderedSet()) return new SequenceValue(fQueryExp.type(), resValues); else return new BagValue(fQueryExp.type(), resValues); }
@Override public StringBuilder toString(StringBuilder sb) { fRangeExp.toString(sb); sb.append("->").append(this.name()).append("("); if (!fElemVarDecls.isEmpty()) { fElemVarDecls.toString(sb); sb.append(" | "); } fQueryExp.toString(sb); return sb.append(")"); }
/** Evaluate sortedBy expressions. */ protected final Value evalSortedBy(EvalContext ctx) { // evaluate range Value v = fRangeExp.eval(ctx); if (v.isUndefined()) return UndefinedValue.instance; CollectionValue rangeVal = (CollectionValue) v; ArrayList<KeyValPair> keyValList = new ArrayList<KeyValPair>(); // loop over range elements for (Value elemVal : rangeVal) { // bind element variable to range element, if variable was // declared if (!fElemVarDecls.isEmpty()) ctx.pushVarBinding(fElemVarDecls.varDecl(0).name(), elemVal); // evaluate sortedBy expression and store the result as a // key together with elemVal Value key = fQueryExp.eval(ctx); keyValList.add(new KeyValPair(key, elemVal)); if (!fElemVarDecls.isEmpty()) ctx.popVarBinding(); } // sort elements by key Collections.sort( keyValList, new Comparator<KeyValPair>() { public int compare(KeyValPair o1, KeyValPair o2) { return o1.fKey.compareTo(o2.fKey); } }); // drop the keys from the list ListIterator<KeyValPair> listIter = keyValList.listIterator(); Collection<Value> result = new ArrayList<Value>(keyValList.size()); while (listIter.hasNext()) { KeyValPair kvp = listIter.next(); result.add(kvp.fElem); } Type rangeElemType = ((CollectionType) fRangeExp.type()).elemType(); return new SequenceValue(rangeElemType, result); }
/** Evaluate select and reject expressions. */ protected final Value evalSelectOrReject(EvalContext ctx, boolean doSelect) { // evaluate range Value v = fRangeExp.eval(ctx); if (v.isUndefined()) return UndefinedValue.instance; CollectionValue rangeVal = (CollectionValue) v; // prepare result value ArrayList<Value> resValues = new ArrayList<Value>(); Type elemType = rangeVal.elemType(); if (!rangeVal.type().isInstantiableCollection()) throw new RuntimeException("rangeVal is not of collection type: " + rangeVal.type()); // loop over range elements for (Value elemVal : rangeVal) { // bind element variable to range element, if variable was // declared if (!fElemVarDecls.isEmpty()) ctx.pushVarBinding(fElemVarDecls.varDecl(0).name(), elemVal); // evaluate select expression Value queryVal = fQueryExp.eval(ctx); // undefined query values default to false if (queryVal.isUndefined()) queryVal = BooleanValue.FALSE; if (((BooleanValue) queryVal).value() == doSelect) resValues.add(elemVal); if (!fElemVarDecls.isEmpty()) ctx.popVarBinding(); } CollectionValue res; if (rangeVal.type().isSet()) res = new SetValue(elemType, resValues); else if (rangeVal.type().isSequence()) res = new SequenceValue(elemType, resValues); else if (rangeVal.type().isBag()) res = new BagValue(elemType, resValues); else if (rangeVal.type().isOrderedSet()) res = new OrderedSetValue(elemType, resValues); else { // should not happen throw new RuntimeException("rangeVal is not of collection type: " + rangeVal.type()); } // result is collection with selected/rejected values return res; }
private final boolean evalExistsOrForAll0( int nesting, CollectionValue rangeVal, EvalContext ctx, boolean doExists) { // loop over range elements boolean res = !doExists; for (Value elemVal : rangeVal) { // bind element variable to range element, if variable was // declared if (!fElemVarDecls.isEmpty()) ctx.pushVarBinding(fElemVarDecls.varDecl(nesting).name(), elemVal); if (!fElemVarDecls.isEmpty() && nesting < fElemVarDecls.size() - 1) { // call recursively to iterate over range while // assigning each value to each element variable // eventually if (res != doExists) res = evalExistsOrForAll0(nesting + 1, rangeVal, ctx, doExists); else // don't change the result value when expression is true // (exists) or // false (forAll) and continue iteration evalExistsOrForAll0(nesting + 1, rangeVal, ctx, doExists); } else { // evaluate predicate expression Value queryVal = fQueryExp.eval(ctx); // undefined query values default to false if (queryVal.isUndefined()) queryVal = BooleanValue.FALSE; // don't change the result value when expression is true // (exists) or // false (forAll) and continue iteration if (res != doExists && ((BooleanValue) queryVal).value() == doExists) res = doExists; } if (!fElemVarDecls.isEmpty()) ctx.popVarBinding(); } return res; }