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