|
1 /* |
|
2 * reserved comment block |
|
3 * DO NOT REMOVE OR ALTER! |
|
4 */ |
|
5 /* |
|
6 * Copyright 2001-2004 The Apache Software Foundation. |
|
7 * |
|
8 * Licensed under the Apache License, Version 2.0 (the "License"); |
|
9 * you may not use this file except in compliance with the License. |
|
10 * 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 java.util.Vector; |
|
27 |
|
28 import com.sun.org.apache.bcel.internal.generic.BranchHandle; |
|
29 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen; |
|
30 import com.sun.org.apache.bcel.internal.generic.GOTO; |
|
31 import com.sun.org.apache.bcel.internal.generic.IFEQ; |
|
32 import com.sun.org.apache.bcel.internal.generic.IFGE; |
|
33 import com.sun.org.apache.bcel.internal.generic.IFGT; |
|
34 import com.sun.org.apache.bcel.internal.generic.ILOAD; |
|
35 import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE; |
|
36 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL; |
|
37 import com.sun.org.apache.bcel.internal.generic.ISTORE; |
|
38 import com.sun.org.apache.bcel.internal.generic.InstructionHandle; |
|
39 import com.sun.org.apache.bcel.internal.generic.InstructionList; |
|
40 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen; |
|
41 import com.sun.org.apache.bcel.internal.generic.PUSH; |
|
42 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator; |
|
43 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg; |
|
44 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator; |
|
45 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeSetType; |
|
46 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.StringType; |
|
47 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type; |
|
48 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError; |
|
49 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util; |
|
50 import com.sun.org.apache.xml.internal.dtm.Axis; |
|
51 import com.sun.org.apache.xml.internal.utils.XML11Char; |
|
52 |
|
53 /** |
|
54 * @author Morten Jorgensen |
|
55 * @author Santiago Pericas-Geertsen |
|
56 */ |
|
57 final 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 il.getEnd(), null); |
|
170 |
|
171 // Get the 'parameter' from the stack and store it in a local var. |
|
172 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 il.append(new ILOAD(parentNode.getIndex())); |
|
190 |
|
191 // Now get the node value and feck 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"+OBJECT_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 } |