KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xalan > 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 org.apache.xalan.xsltc.compiler;
21
22 import java.util.Vector JavaDoc;
23
24 import org.apache.bcel.generic.ALOAD;
25 import org.apache.bcel.generic.ASTORE;
26 import org.apache.bcel.generic.BranchHandle;
27 import org.apache.bcel.generic.ConstantPoolGen;
28 import org.apache.bcel.generic.GOTO;
29 import org.apache.bcel.generic.IFGT;
30 import org.apache.bcel.generic.INVOKEINTERFACE;
31 import org.apache.bcel.generic.INVOKESPECIAL;
32 import org.apache.bcel.generic.INVOKEVIRTUAL;
33 import org.apache.bcel.generic.InstructionHandle;
34 import org.apache.bcel.generic.InstructionList;
35 import org.apache.bcel.generic.LocalVariableGen;
36 import org.apache.bcel.generic.NEW;
37 import org.apache.bcel.generic.PUSH;
38 import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
39 import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
40 import org.apache.xalan.xsltc.compiler.util.StringType;
41 import org.apache.xalan.xsltc.compiler.util.Type;
42 import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
43 import org.apache.xalan.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     /**
101      * Type check the parameters for the id() or key() function.
102      * The index name (for key() call only) must be a string or convertable
103      * to a string, and the lookup-value must be a string or a node-set.
104      * @param stable The parser's symbol table
105      * @throws TypeCheckError When the parameters have illegal type
106      */

107     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
108     final Type returnType = super.typeCheck(stable);
109
110     // Run type check on the key name (first argument) - must be a string,
111
// and if it is not it must be converted to one using string() rules.
112
if (_name != null) {
113         final Type nameType = _name.typeCheck(stable);
114
115         if (_name instanceof LiteralExpr) {
116         final LiteralExpr literal = (LiteralExpr) _name;
117         _resolvedQName =
118             getParser().getQNameIgnoreDefaultNs(literal.getValue());
119         }
120         else if (nameType instanceof StringType == false) {
121         _name = new CastExpr(_name, Type.String);
122         }
123     }
124
125     // Run type check on the value for this key. This value can be of
126
// any data type, so this should never cause any type-check errors.
127
// If the value is not a node-set then it should be converted to a
128
// string before the lookup is done. If the value is a node-set then
129
// this process (convert to string, then do lookup) should be applied
130
// to every node in the set, and the result from all lookups should
131
// be added to the resulting node-set.
132
_valueType = _value.typeCheck(stable);
133
134     if (_valueType != Type.NodeSet && _valueType != Type.String)
135     {
136         _value = new CastExpr(_value, Type.String);
137     }
138
139     return returnType;
140     }
141
142     /**
143      * This method is called when the constructor is compiled in
144      * Stylesheet.compileConstructor() and not as the syntax tree is traversed.
145      * This method is a wrapper for the real translation method, which is
146      * the private method translateCall() below. All this method does is to
147      * wrap the KeyIndex that this function returns inside a duplicate filter.
148      * The duplicate filter is used both to eliminate duplicates and to
149      * cache the nodes in the index.
150      * @param classGen The Java class generator
151      * @param methodGen The method generator
152      */

153     public void translate(ClassGenerator classGen,
154               MethodGenerator methodGen) {
155     final ConstantPoolGen cpg = classGen.getConstantPool();
156     final InstructionList il = methodGen.getInstructionList();
157                          
158     final int getNodeHandle = cpg.addInterfaceMethodref(DOM_INTF,
159                                "getNodeHandle",
160                                "(I)"+NODE_SIG);
161
162     // Wrap the KeyIndex (iterator) inside a duplicate filter iterator
163
// to pre-read the indexed nodes and cache them.
164
final int dupInit = cpg.addMethodref(DUP_FILTERED_ITERATOR,
165                          "<init>",
166                          "("+NODE_ITERATOR_SIG+")V");
167                          
168     il.append(new NEW(cpg.addClass(DUP_FILTERED_ITERATOR)));
169     il.append(DUP);
170     translateCall(classGen, methodGen);
171     il.append(new INVOKESPECIAL(dupInit));
172     
173     }
174
175     /**
176      * Translate the actual index lookup - leaves KeyIndex (iterator) on stack
177      * @param classGen The Java class generator
178      * @param methodGen The method generator
179      */

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

271         // Now we're ready to start traversing the node-set given in
272
// the key() function's second argument....
273
final BranchHandle nextNode = il.append(new GOTO(null));
274         final InstructionHandle loop = il.append(NOP);
275
276         // Push returnIndex on stack to prepare for call to merge()
277
il.append(new ALOAD(returnIndex.getIndex()));
278         
279         // Lookup index using the string value from the current node
280
il.append(new ALOAD(searchIndex.getIndex()));
281         il.append(DUP);
282         il.append(methodGen.loadDOM());
283         il.append(methodGen.loadCurrentNode());
284         il.append(new INVOKEINTERFACE(getNodeValue, 2));
285         if (_name == null) {
286         il.append(new INVOKEVIRTUAL(lookupId));
287         }
288         else {
289         il.append(new INVOKEVIRTUAL(lookupKey));
290         }
291
292         // Call to returnIndex.merge(searchIndex);
293
il.append(new INVOKEVIRTUAL(merge));
294         
295         // Go on with next node in the 2nd parameter node-set
296
nextNode.setTarget(il.append(methodGen.loadIterator()));
297         il.append(methodGen.nextNode());
298         il.append(DUP);
299         il.append(methodGen.storeCurrentNode());
300         il.append(new IFGT(loop));
301
302         // LOOP ENDS HERE
303

304         // Restore current node and current iterator from the stack
305
il.append(methodGen.storeIterator());
306         il.append(methodGen.storeCurrentNode());
307
308         // Return with the an iterator for all resulting nodes
309
il.append(new ALOAD(returnIndex.getIndex()));
310     }
311     // If the second parameter is a single value we just lookup the named
312
// index and initialise the iterator to return nodes with this value.
313
else {
314         // Call getKeyIndex in AbstractTranslet with the name of the key
315
// to get the index for this key (which is also a node iterator).
316
il.append(classGen.loadTranslet());
317         if (_name == null) {
318         il.append(new PUSH(cpg,"##id"));
319         }
320         else if (_resolvedQName != null) {
321         il.append(new PUSH(cpg, _resolvedQName.toString()));
322         }
323         else {
324         _name.translate(classGen, methodGen);
325         }
326         il.append(new INVOKEVIRTUAL(getKeyIndex));
327
328         // Now use the value in the second argument to determine what nodes
329
// the iterator should return.
330
il.append(DUP);
331
332         _value.translate(classGen, methodGen);
333
334         if (_name == null) {
335         il.append(new INVOKEVIRTUAL(lookupId));
336         }
337         else {
338         il.append(new INVOKEVIRTUAL(lookupKey));
339         }
340     }
341     }
342 }
343
Popular Tags