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 }
--- EOF ---