001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: KeyCall.java 1225842 2011-12-30 15:14:35Z mrglavas $
020     */
021    
022    package org.apache.xalan.xsltc.compiler;
023    
024    import java.util.Vector;
025    
026    import org.apache.bcel.generic.ConstantPoolGen;
027    import org.apache.bcel.generic.INVOKEVIRTUAL;
028    import org.apache.bcel.generic.InstructionList;
029    import org.apache.bcel.generic.PUSH;
030    import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
031    import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
032    import org.apache.xalan.xsltc.compiler.util.StringType;
033    import org.apache.xalan.xsltc.compiler.util.Type;
034    import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
035    
036    /**
037     * @author Morten Jorgensen
038     * @author Santiago Pericas-Geertsen
039     */
040    final class KeyCall extends FunctionCall {
041    
042        /**
043         * The name of the key.
044         */
045        private Expression _name;
046    
047        /**
048         * The value to look up in the key/index.
049         */
050        private Expression _value;
051    
052        /**
053         * The value's data type.
054         */
055        private Type _valueType; // The value's data type
056    
057        /**
058         * Expanded qname when name is literal.
059         */
060        private QName _resolvedQName = null;
061    
062        /**
063         * Get the parameters passed to function:
064         *   key(String name, String value)
065         *   key(String name, NodeSet value)
066         * The 'arguments' vector should contain two parameters for key() calls,
067         * one holding the key name and one holding the value(s) to look up. The
068         * vector has only one parameter for id() calls (the key name is always
069         * "##id" for id() calls).
070         *
071         * @param fname The function name (should be 'key' or 'id')
072         * @param arguments A vector containing the arguments the the function
073         */
074        public KeyCall(QName fname, Vector arguments) {
075            super(fname, arguments);
076            switch(argumentCount()) {
077            case 1:
078                _name = null;
079                _value = argument(0);
080                break;
081            case 2:
082                _name = argument(0);
083                _value = argument(1);
084                break;
085            default:
086                _name = _value = null;
087                break;
088            }
089        }
090    
091         /**
092         * If this call to key() is in a top-level element like  another variable
093         * or param, add a dependency between that top-level element and the 
094         * referenced key. For example,
095         *
096         *   <xsl:key name="x" .../>
097         *   <xsl:variable name="y" select="key('x', 1)"/>
098         *
099         * and assuming this class represents "key('x', 1)", add a reference 
100         * between variable y and key x. Note that if 'x' is unknown statically
101         * in key('x', 1), there's nothing we can do at this point.
102         */
103        public void addParentDependency() {
104            // If name unknown statically, there's nothing we can do
105            if (_resolvedQName == null) return;
106            
107            SyntaxTreeNode node = this;
108            while (node != null && node instanceof TopLevelElement == false) {
109                node = node.getParent();
110            }
111            
112            TopLevelElement parent = (TopLevelElement) node;        
113            if (parent != null) {
114                parent.addDependency(getSymbolTable().getKey(_resolvedQName));
115            }        
116        }
117        
118       /**
119         * Type check the parameters for the id() or key() function.
120         * The index name (for key() call only) must be a string or convertable
121         * to a string, and the lookup-value must be a string or a node-set.
122         * @param stable The parser's symbol table
123         * @throws TypeCheckError When the parameters have illegal type
124         */
125        public Type typeCheck(SymbolTable stable) throws TypeCheckError {
126            final Type returnType = super.typeCheck(stable);
127    
128            // Run type check on the key name (first argument) - must be a string,
129            // and if it is not it must be converted to one using string() rules.
130            if (_name != null) {
131                final Type nameType = _name.typeCheck(stable); 
132    
133                if (_name instanceof LiteralExpr) {
134                    final LiteralExpr literal = (LiteralExpr) _name;
135                    _resolvedQName = 
136                        getParser().getQNameIgnoreDefaultNs(literal.getValue());
137                }
138                else if (nameType instanceof StringType == false) {
139                    _name = new CastExpr(_name, Type.String);
140                }
141            }
142    
143            // Run type check on the value for this key. This value can be of
144            // any data type, so this should never cause any type-check errors.
145            // If the value is a reference, then we have to defer the decision
146            // of how to process it until run-time.
147            // If the value is known not to be a node-set, then it should be
148            // converted to a string before the lookup is done. If the value is 
149            // known to be a node-set then this process (convert to string, then
150            // do lookup) should be applied to every node in the set, and the
151            // result from all lookups should be added to the resulting node-set.
152            _valueType = _value.typeCheck(stable);
153    
154            if (_valueType != Type.NodeSet
155                    && _valueType != Type.Reference
156                    && _valueType != Type.String) {
157                _value = new CastExpr(_value, Type.String);
158                _valueType = _value.typeCheck(stable);
159            }
160    
161        // If in a top-level element, create dependency to the referenced key
162        addParentDependency();
163        
164            return returnType;
165        }
166    
167        /**
168         * This method is called when the constructor is compiled in
169         * Stylesheet.compileConstructor() and not as the syntax tree is traversed.
170         * <p>This method will generate byte code that produces an iterator
171         * for the nodes in the node set for the key or id function call.
172         * @param classGen The Java class generator
173         * @param methodGen The method generator
174         */
175        public void translate(ClassGenerator classGen,
176                              MethodGenerator methodGen) {
177            final ConstantPoolGen cpg = classGen.getConstantPool();
178            final InstructionList il = methodGen.getInstructionList();
179    
180            // Returns the KeyIndex object of a given name
181            final int getKeyIndex = cpg.addMethodref(TRANSLET_CLASS,
182                                                     "getKeyIndex",
183                                                     "(Ljava/lang/String;)"+
184                                                     KEY_INDEX_SIG);
185    
186            // KeyIndex.setDom(Dom) => void
187            final int keyDom = cpg.addMethodref(KEY_INDEX_CLASS,
188                                                "setDom",
189                                                "("+DOM_INTF_SIG+")V");
190    
191            // Initialises a KeyIndex to return nodes with specific values
192            final int getKeyIterator =
193                            cpg.addMethodref(KEY_INDEX_CLASS,
194                                             "getKeyIndexIterator",
195                                             "(" + _valueType.toSignature() + "Z)"
196                                                 + KEY_INDEX_ITERATOR_SIG);
197    
198            // Initialise the index specified in the first parameter of key()
199            il.append(classGen.loadTranslet());
200            if (_name == null) {
201                il.append(new PUSH(cpg,"##id"));
202            } else if (_resolvedQName != null) {
203                il.append(new PUSH(cpg, _resolvedQName.toString()));
204            } else {
205                _name.translate(classGen, methodGen);
206            }
207    
208            // Generate following byte code:
209            //
210            //   KeyIndex ki = translet.getKeyIndex(_name)
211            //   ki.setDom(translet.dom);
212            //   ki.getKeyIndexIterator(_value, true)  - for key()
213            //        OR
214            //   ki.getKeyIndexIterator(_value, false)  - for id()
215            il.append(new INVOKEVIRTUAL(getKeyIndex));
216            il.append(DUP);
217            il.append(methodGen.loadDOM());
218            il.append(new INVOKEVIRTUAL(keyDom));
219    
220            _value.translate(classGen, methodGen);
221            il.append((_name != null) ? ICONST_1: ICONST_0);
222            il.append(new INVOKEVIRTUAL(getKeyIterator));
223        }
224    }