1/* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 */ 4/* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20/* 21 * $Id: Key.java,v 1.6 2006/04/25 02:25:08 jeffsuttor Exp $ 22 */ 23 24package com.sun.org.apache.xalan.internal.xsltc.compiler; 25 26import java.util.Vector; 27 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.IFEQ; 32import com.sun.org.apache.bcel.internal.generic.IFGE; 33import com.sun.org.apache.bcel.internal.generic.IFGT; 34import com.sun.org.apache.bcel.internal.generic.ILOAD; 35import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE; 36import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL; 37import com.sun.org.apache.bcel.internal.generic.ISTORE; 38import com.sun.org.apache.bcel.internal.generic.InstructionHandle; 39import com.sun.org.apache.bcel.internal.generic.InstructionList; 40import com.sun.org.apache.bcel.internal.generic.LocalVariableGen; 41import com.sun.org.apache.bcel.internal.generic.PUSH; 42import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator; 43import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg; 44import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator; 45import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeSetType; 46import com.sun.org.apache.xalan.internal.xsltc.compiler.util.StringType; 47import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type; 48import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError; 49import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util; 50import com.sun.org.apache.xml.internal.dtm.Axis; 51import com.sun.org.apache.xml.internal.utils.XML11Char; 52 53/** 54 * @author Morten Jorgensen 55 * @author Santiago Pericas-Geertsen 56 */ 57final class Key extends TopLevelElement { 58 59 /** 60 * The name of this key as defined in xsl:key. 61 */ 62 private QName _name; 63 64 /** 65 * The pattern to match starting at the root node. 66 */ 67 private Pattern _match; 68 69 /** 70 * The expression that generates the values for this key. 71 */ 72 private Expression _use; 73 74 /** 75 * The type of the _use expression. 76 */ 77 private Type _useType; 78 79 /** 80 * Parse the <xsl:key> element and attributes 81 * @param parser A reference to the stylesheet parser 82 */ 83 public void parseContents(Parser parser) { 84 85 // Get the required attributes and parser XPath expressions 86 final String name = getAttribute("name"); 87 if (!XML11Char.isXML11ValidQName(name)){ 88 ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, name, this); 89 parser.reportError(Constants.ERROR, err); 90 } 91 92 // Parse key name and add to symbol table 93 _name = parser.getQNameIgnoreDefaultNs(name); 94 getSymbolTable().addKey(_name, this); 95 96 _match = parser.parsePattern(this, "match", null); 97 _use = parser.parseExpression(this, "use", null); 98 99 // Make sure required attribute(s) have been set 100 if (_name == null) { 101 reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "name"); 102 return; 103 } 104 if (_match.isDummy()) { 105 reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "match"); 106 return; 107 } 108 if (_use.isDummy()) { 109 reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "use"); 110 return; 111 } 112 } 113 114 /** 115 * Returns a String-representation of this key's name 116 * @return The key's name (from the <xsl:key> elements 'name' attribute). 117 */ 118 public String getName() { 119 return _name.toString(); 120 } 121 122 public Type typeCheck(SymbolTable stable) throws TypeCheckError { 123 // Type check match pattern 124 _match.typeCheck(stable); 125 126 // Cast node values to string values (except for nodesets) 127 _useType = _use.typeCheck(stable); 128 if (_useType instanceof StringType == false && 129 _useType instanceof NodeSetType == false) 130 { 131 _use = new CastExpr(_use, Type.String); 132 } 133 134 return Type.Void; 135 } 136 137 /** 138 * This method is called if the "use" attribute of the key contains a 139 * node set. In this case we must traverse all nodes in the set and 140 * create one entry in this key's index for each node in the set. 141 */ 142 public void traverseNodeSet(ClassGenerator classGen, 143 MethodGenerator methodGen, 144 int buildKeyIndex) { 145 final ConstantPoolGen cpg = classGen.getConstantPool(); 146 final InstructionList il = methodGen.getInstructionList(); 147 148 // DOM.getStringValueX(nodeIndex) => String 149 final int getNodeValue = cpg.addInterfaceMethodref(DOM_INTF, 150 GET_NODE_VALUE, 151 "(I)"+STRING_SIG); 152 153 final int getNodeIdent = cpg.addInterfaceMethodref(DOM_INTF, 154 "getNodeIdent", 155 "(I)"+NODE_SIG); 156 157 // AbstractTranslet.SetKeyIndexDom(name, Dom) => void 158 final int keyDom = cpg.addMethodref(TRANSLET_CLASS, 159 "setKeyIndexDom", 160 "("+STRING_SIG+DOM_INTF_SIG+")V"); 161 162 163 // This variable holds the id of the node we found with the "match" 164 // attribute of xsl:key. This is the id we store, with the value we 165 // get from the nodes we find here, in the index for this key. 166 final LocalVariableGen parentNode = 167 methodGen.addLocalVariable("parentNode", 168 Util.getJCRefType("I"), 169 null, null); 170 171 // Get the 'parameter' from the stack and store it in a local var. 172 parentNode.setStart(il.append(new ISTORE(parentNode.getIndex()))); 173 174 // Save current node and current iterator on the stack 175 il.append(methodGen.loadCurrentNode()); 176 il.append(methodGen.loadIterator()); 177 178 // Overwrite current iterator with one that gives us only what we want 179 _use.translate(classGen, methodGen); 180 _use.startIterator(classGen, methodGen); 181 il.append(methodGen.storeIterator()); 182 183 final BranchHandle nextNode = il.append(new GOTO(null)); 184 final InstructionHandle loop = il.append(NOP); 185 186 // Prepare to call buildKeyIndex(String name, int node, String value); 187 il.append(classGen.loadTranslet()); 188 il.append(new PUSH(cpg, _name.toString())); 189 parentNode.setEnd(il.append(new ILOAD(parentNode.getIndex()))); 190 191 // Now get the node value and push it on the parameter stack 192 il.append(methodGen.loadDOM()); 193 il.append(methodGen.loadCurrentNode()); 194 il.append(new INVOKEINTERFACE(getNodeValue, 2)); 195 196 // Finally do the call to add an entry in the index for this key. 197 il.append(new INVOKEVIRTUAL(buildKeyIndex)); 198 199 il.append(classGen.loadTranslet()); 200 il.append(new PUSH(cpg, getName())); 201 il.append(methodGen.loadDOM()); 202 il.append(new INVOKEVIRTUAL(keyDom)); 203 204 nextNode.setTarget(il.append(methodGen.loadIterator())); 205 il.append(methodGen.nextNode()); 206 207 il.append(DUP); 208 il.append(methodGen.storeCurrentNode()); 209 il.append(new IFGE(loop)); // Go on to next matching node.... 210 211 // Restore current node and current iterator from the stack 212 il.append(methodGen.storeIterator()); 213 il.append(methodGen.storeCurrentNode()); 214 } 215 216 /** 217 * Gather all nodes that match the expression in the attribute "match" 218 * and add one (or more) entries in this key's index. 219 */ 220 public void translate(ClassGenerator classGen, MethodGenerator methodGen) { 221 222 final ConstantPoolGen cpg = classGen.getConstantPool(); 223 final InstructionList il = methodGen.getInstructionList(); 224 final int current = methodGen.getLocalIndex("current"); 225 226 // AbstractTranslet.buildKeyIndex(name,node_id,value) => void 227 final int key = cpg.addMethodref(TRANSLET_CLASS, 228 "buildKeyIndex", 229 "("+STRING_SIG+"I"+STRING_SIG+")V"); 230 231 // AbstractTranslet.SetKeyIndexDom(name, Dom) => void 232 final int keyDom = cpg.addMethodref(TRANSLET_CLASS, 233 "setKeyIndexDom", 234 "("+STRING_SIG+DOM_INTF_SIG+")V"); 235 236 final int getNodeIdent = cpg.addInterfaceMethodref(DOM_INTF, 237 "getNodeIdent", 238 "(I)"+NODE_SIG); 239 240 // DOM.getAxisIterator(root) => NodeIterator 241 final int git = cpg.addInterfaceMethodref(DOM_INTF, 242 "getAxisIterator", 243 "(I)"+NODE_ITERATOR_SIG); 244 245 il.append(methodGen.loadCurrentNode()); 246 il.append(methodGen.loadIterator()); 247 248 // Get an iterator for all nodes in the DOM 249 il.append(methodGen.loadDOM()); 250 il.append(new PUSH(cpg,Axis.DESCENDANT)); 251 il.append(new INVOKEINTERFACE(git, 2)); 252 253 // Reset the iterator to start with the root node 254 il.append(methodGen.loadCurrentNode()); 255 il.append(methodGen.setStartNode()); 256 il.append(methodGen.storeIterator()); 257 258 // Loop for traversing all nodes in the DOM 259 final BranchHandle nextNode = il.append(new GOTO(null)); 260 final InstructionHandle loop = il.append(NOP); 261 262 // Check if the current node matches the pattern in "match" 263 il.append(methodGen.loadCurrentNode()); 264 _match.translate(classGen, methodGen); 265 _match.synthesize(classGen, methodGen); // Leaves 0 or 1 on stack 266 final BranchHandle skipNode = il.append(new IFEQ(null)); 267 268 // If this is a node-set we must go through each node in the set 269 if (_useType instanceof NodeSetType) { 270 // Pass current node as parameter (we're indexing on that node) 271 il.append(methodGen.loadCurrentNode()); 272 traverseNodeSet(classGen, methodGen, key); 273 } 274 else { 275 il.append(classGen.loadTranslet()); 276 il.append(DUP); 277 il.append(new PUSH(cpg, _name.toString())); 278 il.append(DUP_X1); 279 il.append(methodGen.loadCurrentNode()); 280 _use.translate(classGen, methodGen); 281 il.append(new INVOKEVIRTUAL(key)); 282 283 il.append(methodGen.loadDOM()); 284 il.append(new INVOKEVIRTUAL(keyDom)); 285 } 286 287 // Get the next node from the iterator and do loop again... 288 final InstructionHandle skip = il.append(NOP); 289 290 il.append(methodGen.loadIterator()); 291 il.append(methodGen.nextNode()); 292 il.append(DUP); 293 il.append(methodGen.storeCurrentNode()); 294 il.append(new IFGT(loop)); 295 296 // Restore current node and current iterator from the stack 297 il.append(methodGen.storeIterator()); 298 il.append(methodGen.storeCurrentNode()); 299 300 nextNode.setTarget(skip); 301 skipNode.setTarget(skip); 302 } 303} 304