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 }