~vcs-imports/xena/trunk

« back to all changes in this revision

Viewing changes to ext/src/xalan-j_2_7_1/src/org/apache/xalan/xsltc/compiler/KeyCall.java

  • Committer: matthewoliver
  • Date: 2009-12-10 03:18:07 UTC
  • Revision ID: vcs-imports@canonical.com-20091210031807-l086qguzdlljtkl9
Merged Xena Testing into Xena Stable for the Xena 5 release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Licensed to the Apache Software Foundation (ASF) under one
 
3
 * or more contributor license agreements. See the NOTICE file
 
4
 * distributed with this work for additional information
 
5
 * regarding copyright ownership. The ASF licenses this file
 
6
 * to you under the Apache License, Version 2.0 (the  "License");
 
7
 * you may not use this file except in compliance with the License.
 
8
 * You may obtain a copy of the License at
 
9
 *
 
10
 *     http://www.apache.org/licenses/LICENSE-2.0
 
11
 *
 
12
 * Unless required by applicable law or agreed to in writing, software
 
13
 * distributed under the License is distributed on an "AS IS" BASIS,
 
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
15
 * See the License for the specific language governing permissions and
 
16
 * limitations under the License.
 
17
 */
 
18
/*
 
19
 * $Id: KeyCall.java,v 1.2 2009/12/10 03:18:18 matthewoliver Exp $
 
20
 */
 
21
 
 
22
package org.apache.xalan.xsltc.compiler;
 
23
 
 
24
import java.util.Vector;
 
25
 
 
26
import org.apache.bcel.generic.ALOAD;
 
27
import org.apache.bcel.generic.ASTORE;
 
28
import org.apache.bcel.generic.BranchHandle;
 
29
import org.apache.bcel.generic.ConstantPoolGen;
 
30
import org.apache.bcel.generic.GOTO;
 
31
import org.apache.bcel.generic.IFGT;
 
32
import org.apache.bcel.generic.INVOKEINTERFACE;
 
33
import org.apache.bcel.generic.INVOKESPECIAL;
 
34
import org.apache.bcel.generic.INVOKEVIRTUAL;
 
35
import org.apache.bcel.generic.InstructionHandle;
 
36
import org.apache.bcel.generic.InstructionList;
 
37
import org.apache.bcel.generic.LocalVariableGen;
 
38
import org.apache.bcel.generic.NEW;
 
39
import org.apache.bcel.generic.PUSH;
 
40
import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
 
41
import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
 
42
import org.apache.xalan.xsltc.compiler.util.StringType;
 
43
import org.apache.xalan.xsltc.compiler.util.Type;
 
44
import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
 
45
import org.apache.xalan.xsltc.compiler.util.Util;
 
46
 
 
47
/**
 
48
 * @author Morten Jorgensen
 
49
 * @author Santiago Pericas-Geertsen
 
50
 */
 
51
final 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) => void
 
198
        final int keyDom = cpg.addMethodref(KEY_INDEX_CLASS,
 
199
                                            "setDom",
 
200
                                            "("+DOM_INTF_SIG+")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(new INVOKEVIRTUAL(keyDom));
 
230
 
 
231
        _value.translate(classGen, methodGen);
 
232
        il.append((_name != null) ? ICONST_1: ICONST_0);
 
233
        il.append(new INVOKEVIRTUAL(getKeyIterator));
 
234
    }
 
235
}