KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xalan > xsltc > compiler > Key


1 /*
2  * Copyright 2001-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 /*
17  * $Id: Key.java,v 1.18 2004/02/24 03:55:47 zongaro Exp $
18  */

19
20 package org.apache.xalan.xsltc.compiler;
21
22 import org.apache.bcel.generic.BranchHandle;
23 import org.apache.bcel.generic.ConstantPoolGen;
24 import org.apache.bcel.generic.GOTO;
25 import org.apache.bcel.generic.IFEQ;
26 import org.apache.bcel.generic.IFGE;
27 import org.apache.bcel.generic.IFGT;
28 import org.apache.bcel.generic.ILOAD;
29 import org.apache.bcel.generic.INVOKEINTERFACE;
30 import org.apache.bcel.generic.INVOKEVIRTUAL;
31 import org.apache.bcel.generic.ISTORE;
32 import org.apache.bcel.generic.InstructionHandle;
33 import org.apache.bcel.generic.InstructionList;
34 import org.apache.bcel.generic.LocalVariableGen;
35 import org.apache.bcel.generic.PUSH;
36 import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
37 import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
38 import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
39 import org.apache.xalan.xsltc.compiler.util.NodeSetType;
40 import org.apache.xalan.xsltc.compiler.util.StringType;
41 import org.apache.xalan.xsltc.compiler.util.Type;
42 import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
43 import org.apache.xalan.xsltc.compiler.util.Util;
44 import org.apache.xalan.xsltc.dom.Axis;
45 import org.apache.xml.utils.XMLChar;
46
47 /**
48  * @author Morten Jorgensen
49  * @author Santiago Pericas-Geertsen
50  */

51 final class Key extends TopLevelElement {
52
53     /**
54      * The name of this key as defined in xsl:key.
55      */

56     private QName _name;
57
58     /**
59      * The pattern to match starting at the root node.
60      */

61     private Pattern _match;
62
63     /**
64      * The expression that generates the values for this key.
65      */

66     private Expression _use;
67
68     /**
69      * The type of the _use expression.
70      */

71     private Type _useType;
72
73     /**
74      * Parse the <xsl:key> element and attributes
75      * @param parser A reference to the stylesheet parser
76      */

77     public void parseContents(Parser parser) {
78
79     // Get the required attributes and parser XPath expressions
80
final String JavaDoc name = getAttribute("name");
81         if (!XMLChar.isValidQName(name)){
82             ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, name, this);
83             parser.reportError(Constants.ERROR, err);
84         }
85         _name = parser.getQNameIgnoreDefaultNs(name);
86     _match = parser.parsePattern(this, "match", null);
87     _use = parser.parseExpression(this, "use", null);
88
89         // Make sure required attribute(s) have been set
90
if (_name == null) {
91         reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "name");
92         return;
93         }
94         if (_match.isDummy()) {
95         reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "match");
96         return;
97         }
98         if (_use.isDummy()) {
99         reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "use");
100         return;
101         }
102     }
103
104     /**
105      * Returns a String-representation of this key's name
106      * @return The key's name (from the <xsl:key> elements 'name' attribute).
107      */

108     public String JavaDoc getName() {
109     return _name.toString();
110     }
111
112     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
113     // Type check match pattern
114
_match.typeCheck(stable);
115
116     // Cast node values to string values (except for nodesets)
117
_useType = _use.typeCheck(stable);
118     if (_useType instanceof StringType == false &&
119         _useType instanceof NodeSetType == false)
120     {
121         _use = new CastExpr(_use, Type.String);
122     }
123
124     return Type.Void;
125     }
126
127     /**
128      * This method is called if the "use" attribute of the key contains a
129      * node set. In this case we must traverse all nodes in the set and
130      * create one entry in this key's index for each node in the set.
131      */

132     public void traverseNodeSet(ClassGenerator classGen,
133                 MethodGenerator methodGen,
134                 int buildKeyIndex) {
135     final ConstantPoolGen cpg = classGen.getConstantPool();
136     final InstructionList il = methodGen.getInstructionList();
137
138     // DOM.getStringValueX(nodeIndex) => String
139
final int getNodeValue = cpg.addInterfaceMethodref(DOM_INTF,
140                                GET_NODE_VALUE,
141                                "(I)"+STRING_SIG);
142                                
143     final int getNodeIdent = cpg.addInterfaceMethodref(DOM_INTF,
144                                "getNodeIdent",
145                                "(I)"+NODE_SIG);
146                                
147     // AbstractTranslet.SetKeyIndexDom(name, Dom) => void
148
final int keyDom = cpg.addMethodref(TRANSLET_CLASS,
149                      "setKeyIndexDom",
150                      "("+STRING_SIG+DOM_INTF_SIG+")V");
151                                                
152
153     // This variable holds the id of the node we found with the "match"
154
// attribute of xsl:key. This is the id we store, with the value we
155
// get from the nodes we find here, in the index for this key.
156
final LocalVariableGen parentNode =
157         methodGen.addLocalVariable("parentNode",
158                        Util.getJCRefType("I"),
159                        il.getEnd(), null);
160
161     // Get the 'parameter' from the stack and store it in a local var.
162
il.append(new ISTORE(parentNode.getIndex()));
163     il.append(methodGen.loadDOM());
164     il.append(new ILOAD(parentNode.getIndex()));
165     il.append(new INVOKEINTERFACE(getNodeIdent, 2));
166     il.append(new ISTORE(parentNode.getIndex()));
167
168     // Save current node and current iterator on the stack
169
il.append(methodGen.loadCurrentNode());
170     il.append(methodGen.loadIterator());
171
172     // Overwrite current iterator with one that gives us only what we want
173
_use.translate(classGen, methodGen);
174     _use.startIterator(classGen, methodGen);
175     il.append(methodGen.storeIterator());
176
177     final BranchHandle nextNode = il.append(new GOTO(null));
178     final InstructionHandle loop = il.append(NOP);
179
180     // Prepare to call buildKeyIndex(String name, int node, String value);
181
il.append(classGen.loadTranslet());
182     il.append(new PUSH(cpg, _name.toString()));
183     il.append(new ILOAD(parentNode.getIndex()));
184
185     // Now get the node value and feck it on the parameter stack
186
il.append(methodGen.loadDOM());
187     il.append(methodGen.loadCurrentNode());
188     il.append(new INVOKEINTERFACE(getNodeValue, 2));
189
190     // Finally do the call to add an entry in the index for this key.
191
il.append(new INVOKEVIRTUAL(buildKeyIndex));
192     
193     il.append(classGen.loadTranslet());
194     il.append(new PUSH(cpg, getName()));
195     il.append(methodGen.loadDOM());
196     il.append(new INVOKEVIRTUAL(keyDom));
197
198     nextNode.setTarget(il.append(methodGen.loadIterator()));
199     il.append(methodGen.nextNode());
200
201     il.append(DUP);
202     il.append(methodGen.storeCurrentNode());
203     il.append(new IFGE(loop)); // Go on to next matching node....
204

205     // Restore current node and current iterator from the stack
206
il.append(methodGen.storeIterator());
207     il.append(methodGen.storeCurrentNode());
208     }
209
210     /**
211      * Gather all nodes that match the expression in the attribute "match"
212      * and add one (or more) entries in this key's index.
213      */

214     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
215
216     final ConstantPoolGen cpg = classGen.getConstantPool();
217     final InstructionList il = methodGen.getInstructionList();
218     final int current = methodGen.getLocalIndex("current");
219
220     // AbstractTranslet.buildKeyIndex(name,node_id,value) => void
221
final int key = cpg.addMethodref(TRANSLET_CLASS,
222                      "buildKeyIndex",
223                      "("+STRING_SIG+"I"+OBJECT_SIG+")V");
224                      
225     // AbstractTranslet.SetKeyIndexDom(name, Dom) => void
226
final int keyDom = cpg.addMethodref(TRANSLET_CLASS,
227                      "setKeyIndexDom",
228                      "("+STRING_SIG+DOM_INTF_SIG+")V");
229                      
230     final int getNodeIdent = cpg.addInterfaceMethodref(DOM_INTF,
231                                "getNodeIdent",
232                                "(I)"+NODE_SIG);
233
234     // DOM.getAxisIterator(root) => NodeIterator
235
final int git = cpg.addInterfaceMethodref(DOM_INTF,
236                           "getAxisIterator",
237                           "(I)"+NODE_ITERATOR_SIG);
238
239     il.append(methodGen.loadCurrentNode());
240     il.append(methodGen.loadIterator());
241
242     // Get an iterator for all nodes in the DOM
243
il.append(methodGen.loadDOM());
244     il.append(new PUSH(cpg,Axis.DESCENDANT));
245     il.append(new INVOKEINTERFACE(git, 2));
246
247     // Reset the iterator to start with the root node
248
il.append(methodGen.loadCurrentNode());
249     il.append(methodGen.setStartNode());
250     il.append(methodGen.storeIterator());
251
252     // Loop for traversing all nodes in the DOM
253
final BranchHandle nextNode = il.append(new GOTO(null));
254     final InstructionHandle loop = il.append(NOP);
255
256     // Check if the current node matches the pattern in "match"
257
il.append(methodGen.loadCurrentNode());
258     _match.translate(classGen, methodGen);
259     _match.synthesize(classGen, methodGen); // Leaves 0 or 1 on stack
260
final BranchHandle skipNode = il.append(new IFEQ(null));
261     
262     // If this is a node-set we must go through each node in the set
263
if (_useType instanceof NodeSetType) {
264         // Pass current node as parameter (we're indexing on that node)
265
il.append(methodGen.loadCurrentNode());
266         traverseNodeSet(classGen, methodGen, key);
267     }
268     else {
269         il.append(classGen.loadTranslet());
270         il.append(DUP);
271         il.append(new PUSH(cpg, _name.toString()));
272         il.append(DUP_X1);
273         il.append(methodGen.loadCurrentNode());
274         _use.translate(classGen, methodGen);
275         il.append(SWAP);
276         il.append(methodGen.loadDOM());
277         il.append(SWAP);
278         il.append(new INVOKEINTERFACE(getNodeIdent, 2));
279         il.append(SWAP);
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 }
303
Popular Tags