KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > gnu > xquery > util > RelativeStep


1 // Copyright (c) 2003 Per M.A. Bothner.
2
// This is free software; for terms and warranty disclaimer see ./COPYING.
3

4 package gnu.xquery.util;
5 import gnu.lists.*;
6 import gnu.mapping.*;
7 import gnu.bytecode.*;
8 import gnu.expr.*;
9 import gnu.kawa.xml.*;
10 import gnu.math.IntNum;
11 import gnu.kawa.functions.*;
12 import gnu.kawa.reflect.OccurrenceType;
13
14 /** Implements XPath path expression.
15  * The XPath expression E1/E2 is compiled into:
16  * (relative-step E1 (lambda (dot position last) E2)).
17  */

18
19 public class RelativeStep extends MethodProc implements CanInline, Inlineable
20 {
21   public static final RelativeStep relativeStep = new RelativeStep();
22
23   public int numArgs() { return 0x2002; }
24
25   public void apply (CallContext ctx) throws Throwable JavaDoc
26   {
27     Object JavaDoc arg = ctx.getNextArg();
28     Object JavaDoc next = ctx.getNextArg();
29     Procedure proc = (Procedure) next;
30     Consumer out = ctx.consumer;
31     IntNum countObj;
32     Nodes values;
33     if (arg instanceof Nodes)
34       values = (Nodes) arg;
35     else
36       {
37     values = new Nodes();
38     Values.writeValues(arg, values);
39       }
40     int count = values.size();
41     int it = 0;
42     countObj = IntNum.make(count);
43     RelativeStepFilter filter = new RelativeStepFilter(out);
44     for (int pos = 1; pos <= count; pos++)
45       {
46     it = values.nextPos(it);
47     Object JavaDoc dot = values.getPosPrevious(it);
48     proc.check3(dot, IntNum.make(pos), countObj, ctx);
49         Values.writeValues(ctx.runUntilValue(), filter);
50       }
51     filter.finish();
52   }
53
54   public Expression inline (ApplyExp exp, ExpWalker walker)
55   {
56     Expression[] args = exp.getArgs();
57     Expression exp1 = args[0];
58     Expression exp2 = args[1];
59     LambdaExp lexp2;
60     Compilation comp = walker.getCompilation();
61     if (! (exp2 instanceof LambdaExp)
62         // The following optimization breaks when interpreting, because
63
// then CoerceToNodes may not work.
64
|| ! comp.mustCompile
65     || (lexp2 = (LambdaExp) exp2).min_args != 3
66     || lexp2.max_args != 3)
67       return exp;
68
69     lexp2.setInlineOnly(true);
70     lexp2.returnContinuation = exp;
71
72     exp2 = lexp2.body;
73
74     Declaration dotArg = lexp2.firstDecl();
75     Declaration posArg = dotArg.nextDecl();
76     Declaration lastArg = posArg.nextDecl();
77     // Splice out the "last" argument - we'll move it out.
78
// The remaining two arguments are suitable for a ValuesMap.
79
posArg.setNext(lastArg.nextDecl());
80     lastArg.setNext(null);
81     lexp2.min_args = 2;
82     lexp2.max_args = 2;
83
84     Type type1 = exp1.getType();
85     Type rtype = exp.getTypeRaw();
86     Type rtypePrime;
87     int nodeCompare;
88     if (rtype == null || rtype == Type.pointer_type)
89       {
90         Type type2 = exp2.getType();
91         rtypePrime = OccurrenceType.itemPrimeType(type2);
92         nodeCompare = NodeType.anyNodeTest.compare(rtypePrime);
93         if (nodeCompare >= 0)
94           rtype = NodeSetType.getInstance(rtypePrime);
95         else
96           rtype = OccurrenceType.getInstance(rtypePrime, 0, -1);
97         exp.setType(rtype);
98       }
99     if (lastArg.getCanRead())
100       {
101         ClassType typeNodes = CoerceNodes.typeNodes;
102         comp.letStart();
103         Declaration sequence
104           = comp.letVariable(null, typeNodes,
105                              new ApplyExp(CoerceNodes.coerceNodes,
106                                           new Expression [] { exp1 }));
107         comp.letEnter();
108
109         Method sizeMethod = typeNodes.getDeclaredMethod("size", 0);
110         Expression lastInit
111           = new ApplyExp(sizeMethod,
112                           new Expression[] {new ReferenceExp(sequence)});
113         LetExp lastLet = new LetExp(new Expression[] { lastInit });
114         lastLet.addDeclaration(lastArg);
115         lastLet.body = new ApplyExp(exp.getFunction(),
116                                     new Expression[] { new ReferenceExp(sequence),
117                                                        lexp2 });
118         return comp.letDone(lastLet);
119       }
120
121     ApplyExp result = exp;
122
123     // Try to rewrite A/B[P] to (A/B)[P].
124
// This only works if P doesn't depend in position() or last().
125
if (exp2 instanceof ApplyExp)
126       {
127         ApplyExp aexp2 = (ApplyExp) exp2;
128         Object JavaDoc proc2 = aexp2.getFunction().valueIfConstant();
129         Expression vexp2;
130         if (proc2 instanceof ValuesFilter
131             && (vexp2 = aexp2.getArgs()[1]) instanceof LambdaExp)
132           {
133             LambdaExp lvexp2 = (LambdaExp) vexp2;
134             Declaration dot2 = lvexp2.firstDecl();
135             Declaration pos2;
136             if (dot2 != null && (pos2 = dot2.nextDecl()) != null
137                 && pos2.nextDecl() == null
138                 && ! pos2.getCanRead()
139                 // If the predicate can evaluate to a number, then the
140
// optimization is unsafe, since we implicitly
141
// compare against position().
142
&& ClassType.make("java.lang.Number").compare(lvexp2.body.getType()) == -3)
143               {
144                 exp2 = aexp2.getArg(0);
145                 lexp2.body = exp2;
146                 aexp2.setArg(0, exp);
147                 result = aexp2;
148               }
149           }
150       }
151     // Now we can rewrite 'descendant-or-self::node()/B' (which is the
152
// expansion of the abbreviated syntax '//B') to /descdendant::B'.
153
if (exp1 instanceof ApplyExp && exp2 instanceof ApplyExp)
154       {
155         ApplyExp aexp1 = (ApplyExp) exp1;
156         ApplyExp aexp2 = (ApplyExp) exp2;
157         Object JavaDoc p1 = aexp1.getFunction().valueIfConstant();
158         Object JavaDoc p2 = aexp2.getFunction().valueIfConstant();
159         Expression exp12;
160         if (p1 == relativeStep && p2 instanceof ChildAxis
161             && aexp1.getArgCount() == 2
162             && (exp12 = aexp1.getArg(1)) instanceof LambdaExp)
163           {
164             LambdaExp lexp12 = (LambdaExp) exp12;
165             ApplyExp aexp12;
166             if (lexp12.body instanceof ApplyExp
167                 && (aexp12 = (ApplyExp) lexp12.body).getFunction().valueIfConstant() == DescendantOrSelfAxis.anyNode)
168               {
169                 exp.setArg(0, aexp1.getArg(0));
170                 aexp2.setFunction(new QuoteExp(DescendantAxis.make(((ChildAxis) p2).getNodePredicate())));
171               }
172           }
173       }
174     
175     return result;
176   }
177
178   public void compile (ApplyExp exp, Compilation comp, Target target)
179   {
180     Expression[] args = exp.getArgs();
181     Expression exp1 = args[0];
182     Expression exp2 = args[1];
183     if (target instanceof IgnoreTarget)
184       {
185         exp1.compile(comp, target);
186         exp2.compile(comp, target);
187         return;
188       }
189
190     Type rtype = exp.getTypeRaw();
191     if (rtype == null) // should never happen
192
rtype = Type.pointer_type;
193     Type rtypePrime = OccurrenceType.itemPrimeType(rtype);
194     int nodeCompare = NodeType.anyNodeTest.compare(rtypePrime);
195     // 'A' - atomic; 'N' - nodes; 'S' - pre-sorted nodes; ' ' - unknown.
196
char expectedKind;
197     if (nodeCompare >= 0)
198       expectedKind = 'N';
199     else if (nodeCompare == -3)
200       expectedKind = 'A';
201     else
202       expectedKind = ' ';
203     TreeScanner step = extractStep(exp2);
204     if (step != null)
205       {
206         Type type1 = exp1.getType();
207         if (step instanceof ChildAxis
208             || step instanceof AttributeAxis
209             || step instanceof SelfAxis)
210           {
211             if (type1 instanceof NodeSetType
212                 || (expectedKind == 'N'
213                     && OccurrenceType.itemCountIsZeroOrOne(exp1.getType())))
214               expectedKind = 'S';
215             /*
216             // It's presumably more efficient to sort the argument
217             // nodes rather than the result nodes. FIXME
218             else
219               {
220                 exp1 = SortNodes(exp1);
221                 expectedKind = 'S';
222               }
223             */

224           }
225       }
226
227     if (! (target instanceof ConsumerTarget
228            || (target instanceof SeriesTarget
229                && (expectedKind == 'A' || expectedKind == 'S'))))
230       {
231     ConsumerTarget.compileUsingConsumer(exp, comp, target);
232     return;
233       }
234
235     CodeAttr code = comp.getCode();
236     Target mtarget;
237     Scope scope = code.pushScope();
238     Variable mconsumer;
239     Variable tconsumer;
240     ClassType mclass;
241
242     if (expectedKind == 'A' || expectedKind == 'S')
243       {
244         mtarget = target;
245         mclass = null;
246         mconsumer = null;
247         tconsumer = null;
248       }
249     else
250       {
251         // We need a helper consumer.
252
Method initMethod;
253         if (expectedKind == 'N')
254           {
255             mclass = ClassType.make("gnu.kawa.xml.SortedNodes");
256             initMethod = mclass.getDeclaredMethod("<init>", 0);
257           }
258         else
259           {
260             mclass = ClassType.make("gnu.xquery.util.RelativeStepFilter");
261             initMethod = mclass.getDeclaredMethod("<init>", 1);
262           }
263         mconsumer = scope.addVariable(code, mclass, null);
264         mtarget = new ConsumerTarget(mconsumer);
265         code.emitNew(mclass);
266         code.emitDup(mclass);
267         tconsumer = ((ConsumerTarget) target).getConsumerVariable();
268         if (expectedKind != 'N')
269           code.emitLoad(tconsumer);
270         code.emitInvoke(initMethod);
271         code.emitStore(mconsumer);
272       }
273
274     ValuesMap.compileInlined((LambdaExp) exp2, exp1, 1, null, comp, mtarget);
275
276     // Now finish up from the helper consumer.
277
if (expectedKind == 'N')
278       {
279         code.emitLoad(mconsumer);
280         code.emitLoad(tconsumer);
281         code.emitInvokeStatic(Compilation.typeValues
282                               .getDeclaredMethod("writeValues", 2));
283       }
284     else if (expectedKind == ' ')
285       {
286         code.emitLoad(mconsumer);
287         code.emitInvoke(mclass.getDeclaredMethod("finish", 0));
288       }
289
290     code.popScope();
291   }
292
293   public Type getReturnType (Expression[] args)
294   {
295     // Needlessly convervative, but it shouldn't matter, since this
296
// shouldn't be called if the ApplyExp.setType has been done.
297
return Type.pointer_type;
298   }
299
300   public static TreeScanner extractStep (Expression exp)
301   {
302     for (;;)
303       {
304         if (! (exp instanceof ApplyExp))
305           return null;
306         ApplyExp aexp = (ApplyExp) exp;
307         Expression func = aexp.getFunction();
308         if (func instanceof QuoteExp)
309           {
310             Object JavaDoc value = ((QuoteExp) func).getValue();
311             if (value instanceof TreeScanner)
312               return (TreeScanner) value;
313             // This doesn't work, if we've already inlined ValuesFilter. FIXME
314
if (value instanceof ValuesFilter)
315               {
316                 exp = aexp.getArgs()[0];
317                 continue;
318               }
319           }
320         return null;
321       }
322   }
323 }
324
Popular Tags