KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > org > apache > xalan > internal > 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 com.sun.org.apache.xalan.internal.xsltc.compiler;
21
22 import java.util.Vector JavaDoc;
23 import com.sun.org.apache.bcel.internal.generic.BranchHandle;
24 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
25 import com.sun.org.apache.bcel.internal.generic.GOTO;
26 import com.sun.org.apache.bcel.internal.generic.IFEQ;
27 import com.sun.org.apache.bcel.internal.generic.IFGE;
28 import com.sun.org.apache.bcel.internal.generic.IFGT;
29 import com.sun.org.apache.bcel.internal.generic.ILOAD;
30 import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
31 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
32 import com.sun.org.apache.bcel.internal.generic.ISTORE;
33 import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
34 import com.sun.org.apache.bcel.internal.generic.InstructionList;
35 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
36 import com.sun.org.apache.bcel.internal.generic.PUSH;
37 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
38 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
39 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
40 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeSetType;
41 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.StringType;
42 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
43 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
44 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
45 import com.sun.org.apache.xalan.internal.xsltc.dom.Axis;
46 import com.sun.org.apache.xml.internal.utils.XMLChar;
47
48 /**
49  * @author Morten Jorgensen
50  * @author Santiago Pericas-Geertsen
51  */

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

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

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

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

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

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

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

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

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

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