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 }