KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > org > apache > xalan > internal > xsltc > compiler > KeyCall


1 /*
2  * Copyright 2001-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 /*
17  * $Id: KeyCall.java,v 1.15 2004/02/16 22:24:29 minchau Exp $
18  */

19
20 package com.sun.org.apache.xalan.internal.xsltc.compiler;
21
22 import java.util.Vector JavaDoc;
23
24 import com.sun.org.apache.bcel.internal.generic.ALOAD;
25 import com.sun.org.apache.bcel.internal.generic.ASTORE;
26 import com.sun.org.apache.bcel.internal.generic.BranchHandle;
27 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
28 import com.sun.org.apache.bcel.internal.generic.GOTO;
29 import com.sun.org.apache.bcel.internal.generic.IFGT;
30 import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
31 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
32 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
33 import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
34 import com.sun.org.apache.bcel.internal.generic.InstructionList;
35 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
36 import com.sun.org.apache.bcel.internal.generic.NEW;
37 import com.sun.org.apache.bcel.internal.generic.PUSH;
38 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
39 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
40 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.StringType;
41 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
42 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
43 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
44
45 /**
46  * @author Morten Jorgensen
47  * @author Santiago Pericas-Geertsen
48  */

49 final class KeyCall extends FunctionCall {
50
51     /**
52      * The name of the key.
53      */

54     private Expression _name;
55
56     /**
57      * The value to look up in the key/index.
58      */

59     private Expression _value;
60
61     /**
62      * The value's data type.
63      */

64     private Type _valueType; // The value's data type
65

66     /**
67      * Expanded qname when name is literal.
68      */

69     private QName _resolvedQName = null;
70
71     /**
72      * Get the parameters passed to function:
73      * key(String name, String value)
74      * key(String name, NodeSet value)
75      * The 'arguments' vector should contain two parameters for key() calls,
76      * one holding the key name and one holding the value(s) to look up. The
77      * vector has only one parameter for id() calls (the key name is always
78      * "##id" for id() calls).
79      *
80      * @param fname The function name (should be 'key' or 'id')
81      * @param arguments A vector containing the arguments the the function
82      */

83     public KeyCall(QName fname, Vector JavaDoc arguments) {
84     super(fname, arguments);
85     switch(argumentCount()) {
86     case 1:
87         _name = null;
88         _value = argument(0);
89         break;
90     case 2:
91         _name = argument(0);
92         _value = argument(1);
93         break;
94     default:
95         _name = _value = null;
96         break;
97     }
98     }
99     /**
100      * If this call to key() is in a top-level element like another variable
101      * or param, add a dependency between that top-level element and the
102      * referenced key. For example,
103      *
104      * <xsl:key name="x" .../>
105      * <xsl:variable name="y" select="key('x', 1)"/>
106      *
107      * and assuming this class represents "key('x', 1)", add a reference
108      * between variable y and key x. Note that if 'x' is unknown statically
109      * in key('x', 1), there's nothing we can do at this point.
110      */

111      public void addParentDependency() {
112         // If name unknown statically, there's nothing we can do
113
if (_resolvedQName == null) return;
114
115     SyntaxTreeNode node = this;
116     while (node != null && node instanceof TopLevelElement == false) {
117         node = node.getParent();
118     }
119         
120     TopLevelElement parent = (TopLevelElement) node;
121     if (parent != null) {
122             parent.addDependency(getSymbolTable().getKey(_resolvedQName));
123     }
124     }
125     /**
126      * Type check the parameters for the id() or key() function.
127      * The index name (for key() call only) must be a string or convertable
128      * to a string, and the lookup-value must be a string or a node-set.
129      * @param stable The parser's symbol table
130      * @throws TypeCheckError When the parameters have illegal type
131      */

132     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
133     final Type returnType = super.typeCheck(stable);
134
135     // Run type check on the key name (first argument) - must be a string,
136
// and if it is not it must be converted to one using string() rules.
137
if (_name != null) {
138         final Type nameType = _name.typeCheck(stable);
139
140         if (_name instanceof LiteralExpr) {
141         final LiteralExpr literal = (LiteralExpr) _name;
142         _resolvedQName =
143             getParser().getQNameIgnoreDefaultNs(literal.getValue());
144         }
145         else if (nameType instanceof StringType == false) {
146         _name = new CastExpr(_name, Type.String);
147         }
148     }
149
150     // Run type check on the value for this key. This value can be of
151
// any data type, so this should never cause any type-check errors.
152
// If the value is not a node-set then it should be converted to a
153
// string before the lookup is done. If the value is a node-set then
154
// this process (convert to string, then do lookup) should be applied
155
// to every node in the set, and the result from all lookups should
156
// be added to the resulting node-set.
157
_valueType = _value.typeCheck(stable);
158
159     if (_valueType != Type.NodeSet && _valueType != Type.String)
160     {
161         _value = new CastExpr(_value, Type.String);
162     }
163         // If in a top-level element, create dependency to the referenced key
164
addParentDependency();
165
166     return returnType;
167     }
168
169     /**
170      * This method is called when the constructor is compiled in
171      * Stylesheet.compileConstructor() and not as the syntax tree is traversed.
172      * This method is a wrapper for the real translation method, which is
173      * the private method translateCall() below. All this method does is to
174      * wrap the KeyIndex that this function returns inside a duplicate filter.
175      * The duplicate filter is used both to eliminate duplicates and to
176      * cache the nodes in the index.
177      * @param classGen The Java class generator
178      * @param methodGen The method generator
179      */

180     public void translate(ClassGenerator classGen,
181               MethodGenerator methodGen) {
182     final ConstantPoolGen cpg = classGen.getConstantPool();
183     final InstructionList il = methodGen.getInstructionList();
184                          
185     final int getNodeHandle = cpg.addInterfaceMethodref(DOM_INTF,
186                                "getNodeHandle",
187                                "(I)"+NODE_SIG);
188
189     // Wrap the KeyIndex (iterator) inside a duplicate filter iterator
190
// to pre-read the indexed nodes and cache them.
191
final int dupInit = cpg.addMethodref(DUP_FILTERED_ITERATOR,
192                          "<init>",
193                          "("+NODE_ITERATOR_SIG+")V");
194                          
195     il.append(new NEW(cpg.addClass(DUP_FILTERED_ITERATOR)));
196     il.append(DUP);
197     translateCall(classGen, methodGen);
198     il.append(new INVOKESPECIAL(dupInit));
199     
200     }
201
202     /**
203      * Translate the actual index lookup - leaves KeyIndex (iterator) on stack
204      * @param classGen The Java class generator
205      * @param methodGen The method generator
206      */

207     private void translateCall(ClassGenerator classGen,
208                   MethodGenerator methodGen) {
209
210     final ConstantPoolGen cpg = classGen.getConstantPool();
211     final InstructionList il = methodGen.getInstructionList();
212
213     // Returns the string value for a node in the DOM
214
final int getNodeValue = cpg.addInterfaceMethodref(DOM_INTF,
215                                GET_NODE_VALUE,
216                                "(I)"+STRING_SIG);
217
218     // Returns the KeyIndex object of a given name
219
final int getKeyIndex = cpg.addMethodref(TRANSLET_CLASS,
220                          "getKeyIndex",
221                          "(Ljava/lang/String;)"+
222                          KEY_INDEX_SIG);
223
224     // Initialises a KeyIndex to return nodes with specific values
225
final int lookupId = cpg.addMethodref(KEY_INDEX_CLASS,
226                           "lookupId",
227                           "(Ljava/lang/Object;)V");
228     final int lookupKey = cpg.addMethodref(KEY_INDEX_CLASS,
229                            "lookupKey",
230                            "(Ljava/lang/Object;)V");
231
232     // Merges the nodes in two KeyIndex objects
233
final int merge = cpg.addMethodref(KEY_INDEX_CLASS,
234                        "merge",
235                        "("+KEY_INDEX_SIG+")V");
236
237     // Constructor for KeyIndex class
238
final int indexConstructor = cpg.addMethodref(TRANSLET_CLASS,
239                               "createKeyIndex",
240                               "()"+KEY_INDEX_SIG);
241                               
242     // KeyIndex.setDom(Dom) => void
243
final int keyDom = cpg.addMethodref(XSLT_PACKAGE + ".dom.KeyIndex",
244                      "setDom",
245                      "("+DOM_INTF_SIG+")V");
246                               
247     
248     // This local variable holds the index/iterator we will return
249
final LocalVariableGen returnIndex =
250         methodGen.addLocalVariable("returnIndex",
251                        Util.getJCRefType(KEY_INDEX_SIG),
252                        il.getEnd(), null);
253
254     // This local variable holds the index we're using for search
255
final LocalVariableGen searchIndex =
256         methodGen.addLocalVariable("searchIndex",
257                        Util.getJCRefType(KEY_INDEX_SIG),
258                        il.getEnd(), null);
259
260     // If the second paramter is a node-set we need to go through each
261
// node in the set, convert each one to a string and do a look up in
262
// the named index, and then merge all the resulting node sets.
263
if (_valueType == Type.NodeSet) {
264         // Save current node and current iterator on the stack
265
il.append(methodGen.loadCurrentNode());
266         il.append(methodGen.loadIterator());
267
268         // Get new iterator from 2nd parameter node-set & store in variable
269
_value.translate(classGen, methodGen);
270         _value.startIterator(classGen, methodGen);
271         il.append(methodGen.storeIterator());
272
273         // Create the KeyIndex object (the iterator) we'll return
274
il.append(classGen.loadTranslet());
275         il.append(new INVOKEVIRTUAL(indexConstructor));
276         il.append(DUP);
277         il.append(methodGen.loadDOM());
278         il.append(new INVOKEVIRTUAL(keyDom));
279         il.append(new ASTORE(returnIndex.getIndex()));
280
281         // Initialise the index specified in the first parameter of key()
282
il.append(classGen.loadTranslet());
283         if (_name == null) {
284         il.append(new PUSH(cpg,"##id"));
285         }
286         else if (_resolvedQName != null) {
287         il.append(new PUSH(cpg, _resolvedQName.toString()));
288         }
289         else {
290         _name.translate(classGen, methodGen);
291         }
292
293         il.append(new INVOKEVIRTUAL(getKeyIndex));
294         il.append(new ASTORE(searchIndex.getIndex()));
295
296         // LOOP STARTS HERE
297

298         // Now we're ready to start traversing the node-set given in
299
// the key() function's second argument....
300
final BranchHandle nextNode = il.append(new GOTO(null));
301         final InstructionHandle loop = il.append(NOP);
302
303         // Push returnIndex on stack to prepare for call to merge()
304
il.append(new ALOAD(returnIndex.getIndex()));
305         
306         // Lookup index using the string value from the current node
307
il.append(new ALOAD(searchIndex.getIndex()));
308         il.append(DUP);
309         il.append(methodGen.loadDOM());
310         il.append(methodGen.loadCurrentNode());
311         il.append(new INVOKEINTERFACE(getNodeValue, 2));
312         if (_name == null) {
313         il.append(new INVOKEVIRTUAL(lookupId));
314         }
315         else {
316         il.append(new INVOKEVIRTUAL(lookupKey));
317         }
318
319         // Call to returnIndex.merge(searchIndex);
320
il.append(new INVOKEVIRTUAL(merge));
321         
322         // Go on with next node in the 2nd parameter node-set
323
nextNode.setTarget(il.append(methodGen.loadIterator()));
324         il.append(methodGen.nextNode());
325         il.append(DUP);
326         il.append(methodGen.storeCurrentNode());
327         il.append(new IFGT(loop));
328
329         // LOOP ENDS HERE
330

331         // Restore current node and current iterator from the stack
332
il.append(methodGen.storeIterator());
333         il.append(methodGen.storeCurrentNode());
334
335         // Return with the an iterator for all resulting nodes
336
il.append(new ALOAD(returnIndex.getIndex()));
337     }
338     // If the second parameter is a single value we just lookup the named
339
// index and initialise the iterator to return nodes with this value.
340
else {
341         // Call getKeyIndex in AbstractTranslet with the name of the key
342
// to get the index for this key (which is also a node iterator).
343
il.append(classGen.loadTranslet());
344         if (_name == null) {
345         il.append(new PUSH(cpg,"##id"));
346         }
347         else if (_resolvedQName != null) {
348         il.append(new PUSH(cpg, _resolvedQName.toString()));
349         }
350         else {
351         _name.translate(classGen, methodGen);
352         }
353         il.append(new INVOKEVIRTUAL(getKeyIndex));
354
355         // Now use the value in the second argument to determine what nodes
356
// the iterator should return.
357
il.append(DUP);
358
359         _value.translate(classGen, methodGen);
360
361         if (_name == null) {
362         il.append(new INVOKEVIRTUAL(lookupId));
363         }
364         else {
365         il.append(new INVOKEVIRTUAL(lookupKey));
366         }
367     }
368     }
369 }
370
Popular Tags