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