001: // Copyright (c) 2001, 2003 Per M.A. Bothner and Brainfood Inc.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.xquery.util;
005:
006: import gnu.lists.*;
007: import gnu.mapping.*;
008: import gnu.bytecode.*;
009: import gnu.expr.*;
010: import gnu.kawa.xml.*;
011: import gnu.math.IntNum;
012: import gnu.kawa.functions.AddOp;
013: import gnu.kawa.functions.NumberCompare;
014: import gnu.kawa.functions.ValuesMap;
015:
016: public class ValuesFilter extends MethodProc implements CanInline,
017: Inlineable {
018: /** 'F' if following a ForwardStep; 'R' if following a ReverseStep;
019: * 'P' if following a PrimaryExpr. */
020: char kind;
021:
022: public ValuesFilter(char kind) {
023: this .kind = kind;
024: }
025:
026: public static ValuesFilter get(char kind) {
027: if (kind == 'F')
028: return forwardFilter;
029: else if (kind == 'R')
030: return reverseFilter;
031: else
032: return exprFilter;
033: }
034:
035: /** 2 if last() is needed (implicit if kind=='R');
036: * 1 if position() is needed;
037: * 0 otherwise. */
038: int last_or_position_needed = 2;
039:
040: public int numArgs() {
041: return 0x2002;
042: }
043:
044: static public boolean matches(Object result, long count) {
045: if (result instanceof Values)
046: result = ((Values) result).canonicalize();
047: if (result instanceof Number) {
048: if (result instanceof IntNum)
049: return IntNum.compare((IntNum) result, count) == 0;
050: if (result instanceof Double || result instanceof Float
051: || result instanceof gnu.math.DFloNum)
052: return ((Number) result).doubleValue() == (double) count;
053: if (result instanceof Long || result instanceof Integer
054: || result instanceof Short
055: || result instanceof Byte)
056: return count == ((Number) result).longValue();
057: // Non-optimal for BigDecimal and BigInteger. FIXME.
058: return NumberCompare.applyWithPromotion(
059: Compare.TRUE_IF_EQU, IntNum.make(count), result);
060: }
061: return BooleanValue.booleanValue(result);
062: }
063:
064: public void apply(CallContext ctx) throws Throwable {
065: Object arg = ctx.getNextArg();
066: Procedure proc = (Procedure) ctx.getNextArg();
067: Consumer out = ctx.consumer;
068: Values values;
069: if (kind != 'P') {
070: SortedNodes nodes = new SortedNodes();
071: Values.writeValues(arg, nodes);
072: values = nodes;
073: } else if (arg instanceof Values)
074: values = (Values) arg;
075: else {
076: IntNum one = IntNum.one();
077: if (matches(proc.apply3(arg, one, one), 1))
078: out.writeObject(arg);
079: return;
080: }
081: int count = values.size();
082: int it = 0;
083: IntNum countObj = IntNum.make(count);
084: // The filter procedures takes 3 arguments is last() is needed,
085: // or 2 arguments if inline has determined we don't need last().
086: int pmax = proc.maxArgs();
087: for (int i = 0; i < count; i++) {
088: it = values.nextPos(it);
089: Object dot = values.getPosPrevious(it);
090: int pos = kind == 'R' ? (count - i) : (i + 1);
091: IntNum posObj = IntNum.make(pos);
092: Object pred_res = pmax == 2 ? proc.apply2(dot, posObj)
093: : proc.apply3(dot, posObj, countObj);
094: if (matches(pred_res, pos))
095: out.writeObject(dot);
096: }
097: return;
098: }
099:
100: public Expression inline(ApplyExp exp, ExpWalker walker) {
101: Expression[] args = exp.getArgs();
102: Expression exp2 = args[1];
103: LambdaExp lexp2;
104: if (!(exp2 instanceof LambdaExp)
105: || (lexp2 = (LambdaExp) exp2).min_args != 3
106: || lexp2.max_args != 3)
107: return exp;
108:
109: exp.setType(args[0].getType());
110:
111: Compilation parser = walker.getCompilation();
112:
113: Declaration dotArg = lexp2.firstDecl();
114: Declaration posArg = dotArg.nextDecl();
115: Declaration lastArg = posArg.nextDecl();
116:
117: lexp2.setInlineOnly(true);
118: lexp2.returnContinuation = exp;
119:
120: // Splice out lastArg
121: lexp2.remove(posArg, lastArg);
122: lexp2.min_args = 2;
123: lexp2.max_args = 2;
124:
125: if (!lastArg.getCanRead() && kind != 'R') {
126: // Don't need to do anything more - lastArg is not needed.
127: return exp;
128: }
129:
130: parser.letStart();
131: Expression seq = args[0];
132: Type seqType;
133: Method sizeMethod;
134: if (kind == 'P') {
135: seqType = seq.getType();
136: sizeMethod = Compilation.typeValues.getDeclaredMethod(
137: "countValues", 1);
138: } else {
139: seqType = SortNodes.typeSortedNodes;
140: seq = new ApplyExp(SortNodes.sortNodes,
141: new Expression[] { seq });
142: sizeMethod = CoerceNodes.typeNodes.getDeclaredMethod(
143: "size", 0);
144: }
145: Declaration sequence = parser.letVariable("sequence", seqType,
146: seq);
147: parser.letEnter();
148:
149: Expression pred = lexp2.body;
150: if (kind == 'R') {
151: Declaration posIncoming = new Declaration(null,
152: Type.int_type);
153: Expression init = new ApplyExp(AddOp.$Mn, new Expression[] {
154: new ReferenceExp(lastArg),
155: new ReferenceExp(posIncoming) });
156: init = new ApplyExp(AddOp.$Pl, new Expression[] { init,
157: new QuoteExp(IntNum.one()) });
158: LetExp let = new LetExp(new Expression[] { init });
159: lexp2.replaceFollowing(dotArg, posIncoming);
160: let.add(posArg);
161: let.body = pred;
162: pred = let;
163: }
164:
165: Type predType = lexp2.body.getType();
166: if (predType != XDataType.booleanType) // Overly conservative, but simple.
167: pred = new ApplyExp(matchesMethod, new Expression[] { pred,
168: new ReferenceExp(posArg) });
169: pred = new IfExp(pred, new ReferenceExp(dotArg),
170: QuoteExp.voidExp);
171: lexp2.body = pred;
172:
173: ApplyExp doMap = new ApplyExp(ValuesMap.valuesMapWithPos,
174: new Expression[] { lexp2, new ReferenceExp(sequence) });
175: doMap.setType(dotArg.getType());
176: lexp2.returnContinuation = doMap;
177:
178: Expression lastInit = new ApplyExp(sizeMethod,
179: new Expression[] { new ReferenceExp(sequence) });
180:
181: LetExp let2 = new LetExp(new Expression[] { lastInit });
182: let2.add(lastArg);
183: let2.body = ValuesMap.valuesMapWithPos.inline(doMap, walker);
184:
185: return parser.letDone(let2);
186: }
187:
188: public void compile(ApplyExp exp, Compilation comp, Target target) {
189: Expression[] args = exp.getArgs();
190: Expression exp1 = args[0];
191: Expression exp2 = args[1];
192: if (target instanceof IgnoreTarget) {
193: exp1.compile(comp, target);
194: exp2.compile(comp, target);
195: return;
196: }
197: if (!(exp2 instanceof LambdaExp)) {
198: ApplyExp.compile(exp, comp, target);
199: return;
200: }
201:
202: if (!(target instanceof ConsumerTarget)) {
203: ConsumerTarget.compileUsingConsumer(exp, comp, target);
204: return;
205: }
206: // Should also handle SeriesTarget - later? FIXME
207:
208: // At this point, we don't depend on last().
209: LambdaExp lexp2 = (LambdaExp) exp2;
210: ValuesMap.compileInlined(lexp2, exp1, 1, matchesMethod, comp,
211: target);
212: }
213:
214: public Type getReturnType(Expression[] args) {
215: // Needlessly conservative, but it shouldn't matter, since this
216: // shouldn't be called if the ApplyExp.setType has been done.
217: return Type.pointer_type;
218: }
219:
220: public static final ValuesFilter forwardFilter = new ValuesFilter(
221: 'F');
222: public static final ValuesFilter reverseFilter = new ValuesFilter(
223: 'R');
224: public static final ValuesFilter exprFilter = new ValuesFilter('P');
225: public static final ClassType typeValuesFilter = ClassType
226: .make("gnu.xquery.util.ValuesFilter");
227: public static final Method matchesMethod = typeValuesFilter
228: .getDeclaredMethod("matches", 2);
229: }
|