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: Key.java 1225842 2011-12-30 15:14:35Z mrglavas $ 020 */ 021 022 package org.apache.xalan.xsltc.compiler; 023 024 import org.apache.bcel.generic.BranchHandle; 025 import org.apache.bcel.generic.ConstantPoolGen; 026 import org.apache.bcel.generic.GOTO; 027 import org.apache.bcel.generic.IFEQ; 028 import org.apache.bcel.generic.IFGE; 029 import org.apache.bcel.generic.IFGT; 030 import org.apache.bcel.generic.ILOAD; 031 import org.apache.bcel.generic.INVOKEINTERFACE; 032 import org.apache.bcel.generic.INVOKEVIRTUAL; 033 import org.apache.bcel.generic.ISTORE; 034 import org.apache.bcel.generic.InstructionHandle; 035 import org.apache.bcel.generic.InstructionList; 036 import org.apache.bcel.generic.LocalVariableGen; 037 import org.apache.bcel.generic.PUSH; 038 import org.apache.xalan.xsltc.compiler.util.ClassGenerator; 039 import org.apache.xalan.xsltc.compiler.util.ErrorMsg; 040 import org.apache.xalan.xsltc.compiler.util.MethodGenerator; 041 import org.apache.xalan.xsltc.compiler.util.NodeSetType; 042 import org.apache.xalan.xsltc.compiler.util.StringType; 043 import org.apache.xalan.xsltc.compiler.util.Type; 044 import org.apache.xalan.xsltc.compiler.util.TypeCheckError; 045 import org.apache.xalan.xsltc.compiler.util.Util; 046 import org.apache.xml.dtm.Axis; 047 import org.apache.xml.utils.XML11Char; 048 049 /** 050 * @author Morten Jorgensen 051 * @author Santiago Pericas-Geertsen 052 */ 053 final class Key extends TopLevelElement { 054 055 /** 056 * The name of this key as defined in xsl:key. 057 */ 058 private QName _name; 059 060 /** 061 * The pattern to match starting at the root node. 062 */ 063 private Pattern _match; 064 065 /** 066 * The expression that generates the values for this key. 067 */ 068 private Expression _use; 069 070 /** 071 * The type of the _use expression. 072 */ 073 private Type _useType; 074 075 /** 076 * Parse the <xsl:key> element and attributes 077 * @param parser A reference to the stylesheet parser 078 */ 079 public void parseContents(Parser parser) { 080 081 // Get the required attributes and parser XPath expressions 082 final String name = getAttribute("name"); 083 if (!XML11Char.isXML11ValidQName(name)){ 084 ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, name, this); 085 parser.reportError(Constants.ERROR, err); 086 } 087 088 // Parse key name and add to symbol table 089 _name = parser.getQNameIgnoreDefaultNs(name); 090 getSymbolTable().addKey(_name, this); 091 092 _match = parser.parsePattern(this, "match", null); 093 _use = parser.parseExpression(this, "use", null); 094 095 // Make sure required attribute(s) have been set 096 if (_name == null) { 097 reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "name"); 098 return; 099 } 100 if (_match.isDummy()) { 101 reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "match"); 102 return; 103 } 104 if (_use.isDummy()) { 105 reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "use"); 106 return; 107 } 108 } 109 110 /** 111 * Returns a String-representation of this key's name 112 * @return The key's name (from the <xsl:key> elements 'name' attribute). 113 */ 114 public String getName() { 115 return _name.toString(); 116 } 117 118 public Type typeCheck(SymbolTable stable) throws TypeCheckError { 119 // Type check match pattern 120 _match.typeCheck(stable); 121 122 // Cast node values to string values (except for nodesets) 123 _useType = _use.typeCheck(stable); 124 if (_useType instanceof StringType == false && 125 _useType instanceof NodeSetType == false) 126 { 127 _use = new CastExpr(_use, Type.String); 128 } 129 130 return Type.Void; 131 } 132 133 /** 134 * This method is called if the "use" attribute of the key contains a 135 * node set. In this case we must traverse all nodes in the set and 136 * create one entry in this key's index for each node in the set. 137 */ 138 public void traverseNodeSet(ClassGenerator classGen, 139 MethodGenerator methodGen, 140 int buildKeyIndex) { 141 final ConstantPoolGen cpg = classGen.getConstantPool(); 142 final InstructionList il = methodGen.getInstructionList(); 143 144 // DOM.getStringValueX(nodeIndex) => String 145 final int getNodeValue = cpg.addInterfaceMethodref(DOM_INTF, 146 GET_NODE_VALUE, 147 "(I)"+STRING_SIG); 148 149 final int getNodeIdent = cpg.addInterfaceMethodref(DOM_INTF, 150 "getNodeIdent", 151 "(I)"+NODE_SIG); 152 153 // AbstractTranslet.SetKeyIndexDom(name, Dom) => void 154 final int keyDom = cpg.addMethodref(TRANSLET_CLASS, 155 "setKeyIndexDom", 156 "("+STRING_SIG+DOM_INTF_SIG+")V"); 157 158 159 // This variable holds the id of the node we found with the "match" 160 // attribute of xsl:key. This is the id we store, with the value we 161 // get from the nodes we find here, in the index for this key. 162 final LocalVariableGen parentNode = 163 methodGen.addLocalVariable("parentNode", 164 Util.getJCRefType("I"), 165 null, null); 166 167 // Get the 'parameter' from the stack and store it in a local var. 168 parentNode.setStart(il.append(new ISTORE(parentNode.getIndex()))); 169 170 // Save current node and current iterator on the stack 171 il.append(methodGen.loadCurrentNode()); 172 il.append(methodGen.loadIterator()); 173 174 // Overwrite current iterator with one that gives us only what we want 175 _use.translate(classGen, methodGen); 176 _use.startIterator(classGen, methodGen); 177 il.append(methodGen.storeIterator()); 178 179 final BranchHandle nextNode = il.append(new GOTO(null)); 180 final InstructionHandle loop = il.append(NOP); 181 182 // Prepare to call buildKeyIndex(String name, int node, String value); 183 il.append(classGen.loadTranslet()); 184 il.append(new PUSH(cpg, _name.toString())); 185 parentNode.setEnd(il.append(new ILOAD(parentNode.getIndex()))); 186 187 // Now get the node value and push it on the parameter stack 188 il.append(methodGen.loadDOM()); 189 il.append(methodGen.loadCurrentNode()); 190 il.append(new INVOKEINTERFACE(getNodeValue, 2)); 191 192 // Finally do the call to add an entry in the index for this key. 193 il.append(new INVOKEVIRTUAL(buildKeyIndex)); 194 195 il.append(classGen.loadTranslet()); 196 il.append(new PUSH(cpg, getName())); 197 il.append(methodGen.loadDOM()); 198 il.append(new INVOKEVIRTUAL(keyDom)); 199 200 nextNode.setTarget(il.append(methodGen.loadIterator())); 201 il.append(methodGen.nextNode()); 202 203 il.append(DUP); 204 il.append(methodGen.storeCurrentNode()); 205 il.append(new IFGE(loop)); // Go on to next matching node.... 206 207 // Restore current node and current iterator from the stack 208 il.append(methodGen.storeIterator()); 209 il.append(methodGen.storeCurrentNode()); 210 } 211 212 /** 213 * Gather all nodes that match the expression in the attribute "match" 214 * and add one (or more) entries in this key's index. 215 */ 216 public void translate(ClassGenerator classGen, MethodGenerator methodGen) { 217 218 final ConstantPoolGen cpg = classGen.getConstantPool(); 219 final InstructionList il = methodGen.getInstructionList(); 220 final int current = methodGen.getLocalIndex("current"); 221 222 // AbstractTranslet.buildKeyIndex(name,node_id,value) => void 223 final int key = cpg.addMethodref(TRANSLET_CLASS, 224 "buildKeyIndex", 225 "("+STRING_SIG+"I"+OBJECT_SIG+")V"); 226 227 // AbstractTranslet.SetKeyIndexDom(name, Dom) => void 228 final int keyDom = cpg.addMethodref(TRANSLET_CLASS, 229 "setKeyIndexDom", 230 "("+STRING_SIG+DOM_INTF_SIG+")V"); 231 232 final int getNodeIdent = cpg.addInterfaceMethodref(DOM_INTF, 233 "getNodeIdent", 234 "(I)"+NODE_SIG); 235 236 // DOM.getAxisIterator(root) => NodeIterator 237 final int git = cpg.addInterfaceMethodref(DOM_INTF, 238 "getAxisIterator", 239 "(I)"+NODE_ITERATOR_SIG); 240 241 il.append(methodGen.loadCurrentNode()); 242 il.append(methodGen.loadIterator()); 243 244 // Get an iterator for all nodes in the DOM 245 il.append(methodGen.loadDOM()); 246 il.append(new PUSH(cpg,Axis.DESCENDANT)); 247 il.append(new INVOKEINTERFACE(git, 2)); 248 249 // Reset the iterator to start with the root node 250 il.append(methodGen.loadCurrentNode()); 251 il.append(methodGen.setStartNode()); 252 il.append(methodGen.storeIterator()); 253 254 // Loop for traversing all nodes in the DOM 255 final BranchHandle nextNode = il.append(new GOTO(null)); 256 final InstructionHandle loop = il.append(NOP); 257 258 // Check if the current node matches the pattern in "match" 259 il.append(methodGen.loadCurrentNode()); 260 _match.translate(classGen, methodGen); 261 _match.synthesize(classGen, methodGen); // Leaves 0 or 1 on stack 262 final BranchHandle skipNode = il.append(new IFEQ(null)); 263 264 // If this is a node-set we must go through each node in the set 265 if (_useType instanceof NodeSetType) { 266 // Pass current node as parameter (we're indexing on that node) 267 il.append(methodGen.loadCurrentNode()); 268 traverseNodeSet(classGen, methodGen, key); 269 } 270 else { 271 il.append(classGen.loadTranslet()); 272 il.append(DUP); 273 il.append(new PUSH(cpg, _name.toString())); 274 il.append(DUP_X1); 275 il.append(methodGen.loadCurrentNode()); 276 _use.translate(classGen, methodGen); 277 il.append(new INVOKEVIRTUAL(key)); 278 279 il.append(methodGen.loadDOM()); 280 il.append(new INVOKEVIRTUAL(keyDom)); 281 } 282 283 // Get the next node from the iterator and do loop again... 284 final InstructionHandle skip = il.append(NOP); 285 286 il.append(methodGen.loadIterator()); 287 il.append(methodGen.nextNode()); 288 il.append(DUP); 289 il.append(methodGen.storeCurrentNode()); 290 il.append(new IFGT(loop)); 291 292 // Restore current node and current iterator from the stack 293 il.append(methodGen.storeIterator()); 294 il.append(methodGen.storeCurrentNode()); 295 296 nextNode.setTarget(skip); 297 skipNode.setTarget(skip); 298 } 299 }