View Javadoc
1   /*
2    * reserved comment block
3    * DO NOT REMOVE OR ALTER!
4    */
5   /*
6    * Copyright 2001-2006 The Apache Software Foundation.
7    *
8    * Licensed under the Apache License, Version 2.0 (the "License");
9    * you may not use this file except in compliance with the License.
10   * You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  /*
21   * $Id: KeyCall.java,v 1.7 2006/06/19 19:49:04 spericas Exp $
22   */
23  
24  package com.sun.org.apache.xalan.internal.xsltc.compiler;
25  
26  import java.util.Vector;
27  
28  import com.sun.org.apache.bcel.internal.generic.ALOAD;
29  import com.sun.org.apache.bcel.internal.generic.ASTORE;
30  import com.sun.org.apache.bcel.internal.generic.BranchHandle;
31  import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
32  import com.sun.org.apache.bcel.internal.generic.GOTO;
33  import com.sun.org.apache.bcel.internal.generic.IFGT;
34  import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
35  import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
36  import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
37  import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
38  import com.sun.org.apache.bcel.internal.generic.InstructionList;
39  import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
40  import com.sun.org.apache.bcel.internal.generic.NEW;
41  import com.sun.org.apache.bcel.internal.generic.PUSH;
42  import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
43  import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
44  import com.sun.org.apache.xalan.internal.xsltc.compiler.util.StringType;
45  import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
46  import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
47  import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
48  
49  /**
50   * @author Morten Jorgensen
51   * @author Santiago Pericas-Geertsen
52   */
53  final class KeyCall extends FunctionCall {
54  
55      /**
56       * The name of the key.
57       */
58      private Expression _name;
59  
60      /**
61       * The value to look up in the key/index.
62       */
63      private Expression _value;
64  
65      /**
66       * The value's data type.
67       */
68      private Type _valueType; // The value's data type
69  
70      /**
71       * Expanded qname when name is literal.
72       */
73      private QName _resolvedQName = null;
74  
75      /**
76       * Get the parameters passed to function:
77       *   key(String name, String value)
78       *   key(String name, NodeSet value)
79       * The 'arguments' vector should contain two parameters for key() calls,
80       * one holding the key name and one holding the value(s) to look up. The
81       * vector has only one parameter for id() calls (the key name is always
82       * "##id" for id() calls).
83       *
84       * @param fname The function name (should be 'key' or 'id')
85       * @param arguments A vector containing the arguments the the function
86       */
87      public KeyCall(QName fname, Vector arguments) {
88          super(fname, arguments);
89          switch(argumentCount()) {
90          case 1:
91              _name = null;
92              _value = argument(0);
93              break;
94          case 2:
95              _name = argument(0);
96              _value = argument(1);
97              break;
98          default:
99              _name = _value = null;
100             break;
101         }
102     }
103 
104      /**
105      * If this call to key() is in a top-level element like  another variable
106      * or param, add a dependency between that top-level element and the
107      * referenced key. For example,
108      *
109      *   <xsl:key name="x" .../>
110      *   <xsl:variable name="y" select="key('x', 1)"/>
111      *
112      * and assuming this class represents "key('x', 1)", add a reference
113      * between variable y and key x. Note that if 'x' is unknown statically
114      * in key('x', 1), there's nothing we can do at this point.
115      */
116     public void addParentDependency() {
117         // If name unknown statically, there's nothing we can do
118         if (_resolvedQName == null) return;
119 
120         SyntaxTreeNode node = this;
121         while (node != null && node instanceof TopLevelElement == false) {
122             node = node.getParent();
123         }
124 
125         TopLevelElement parent = (TopLevelElement) node;
126         if (parent != null) {
127             parent.addDependency(getSymbolTable().getKey(_resolvedQName));
128         }
129     }
130 
131    /**
132      * Type check the parameters for the id() or key() function.
133      * The index name (for key() call only) must be a string or convertable
134      * to a string, and the lookup-value must be a string or a node-set.
135      * @param stable The parser's symbol table
136      * @throws TypeCheckError When the parameters have illegal type
137      */
138     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
139         final Type returnType = super.typeCheck(stable);
140 
141         // Run type check on the key name (first argument) - must be a string,
142         // and if it is not it must be converted to one using string() rules.
143         if (_name != null) {
144             final Type nameType = _name.typeCheck(stable);
145 
146             if (_name instanceof LiteralExpr) {
147                 final LiteralExpr literal = (LiteralExpr) _name;
148                 _resolvedQName =
149                     getParser().getQNameIgnoreDefaultNs(literal.getValue());
150             }
151             else if (nameType instanceof StringType == false) {
152                 _name = new CastExpr(_name, Type.String);
153             }
154         }
155 
156         // Run type check on the value for this key. This value can be of
157         // any data type, so this should never cause any type-check errors.
158         // If the value is a reference, then we have to defer the decision
159         // of how to process it until run-time.
160         // If the value is known not to be a node-set, then it should be
161         // converted to a string before the lookup is done. If the value is
162         // known to be a node-set then this process (convert to string, then
163         // do lookup) should be applied to every node in the set, and the
164         // result from all lookups should be added to the resulting node-set.
165         _valueType = _value.typeCheck(stable);
166 
167         if (_valueType != Type.NodeSet
168                 && _valueType != Type.Reference
169                 && _valueType != Type.String) {
170             _value = new CastExpr(_value, Type.String);
171             _valueType = _value.typeCheck(stable);
172         }
173 
174         // If in a top-level element, create dependency to the referenced key
175         addParentDependency();
176 
177         return returnType;
178     }
179 
180     /**
181      * This method is called when the constructor is compiled in
182      * Stylesheet.compileConstructor() and not as the syntax tree is traversed.
183      * <p>This method will generate byte code that produces an iterator
184      * for the nodes in the node set for the key or id function call.
185      * @param classGen The Java class generator
186      * @param methodGen The method generator
187      */
188     public void translate(ClassGenerator classGen,
189                           MethodGenerator methodGen) {
190         final ConstantPoolGen cpg = classGen.getConstantPool();
191         final InstructionList il = methodGen.getInstructionList();
192 
193         // Returns the KeyIndex object of a given name
194         final int getKeyIndex = cpg.addMethodref(TRANSLET_CLASS,
195                                                  "getKeyIndex",
196                                                  "(Ljava/lang/String;)"+
197                                                  KEY_INDEX_SIG);
198 
199         // KeyIndex.setDom(Dom, node) => void
200         final int keyDom = cpg.addMethodref(KEY_INDEX_CLASS,
201                                             "setDom",
202                                             "(" + DOM_INTF_SIG + "I)V");
203 
204         // Initialises a KeyIndex to return nodes with specific values
205         final int getKeyIterator =
206                         cpg.addMethodref(KEY_INDEX_CLASS,
207                                          "getKeyIndexIterator",
208                                          "(" + _valueType.toSignature() + "Z)"
209                                              + KEY_INDEX_ITERATOR_SIG);
210 
211         // Initialise the index specified in the first parameter of key()
212         il.append(classGen.loadTranslet());
213         if (_name == null) {
214             il.append(new PUSH(cpg,"##id"));
215         } else if (_resolvedQName != null) {
216             il.append(new PUSH(cpg, _resolvedQName.toString()));
217         } else {
218             _name.translate(classGen, methodGen);
219         }
220 
221         // Generate following byte code:
222         //
223         //   KeyIndex ki = translet.getKeyIndex(_name)
224         //   ki.setDom(translet.dom);
225         //   ki.getKeyIndexIterator(_value, true)  - for key()
226         //        OR
227         //   ki.getKeyIndexIterator(_value, false)  - for id()
228         il.append(new INVOKEVIRTUAL(getKeyIndex));
229         il.append(DUP);
230         il.append(methodGen.loadDOM());
231         il.append(methodGen.loadCurrentNode());
232         il.append(new INVOKEVIRTUAL(keyDom));
233 
234         _value.translate(classGen, methodGen);
235         il.append((_name != null) ? ICONST_1: ICONST_0);
236         il.append(new INVOKEVIRTUAL(getKeyIterator));
237     }
238 }