6
|
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: SyntaxTreeNode.java,v 1.6 2006/06/06 22:34:33 spericas Exp $
|
|
22 |
*/
|
|
23 |
|
|
24 |
package com.sun.org.apache.xalan.internal.xsltc.compiler;
|
|
25 |
|
|
26 |
import java.util.Enumeration;
|
|
27 |
import java.util.Hashtable;
|
|
28 |
import java.util.Vector;
|
|
29 |
|
|
30 |
import com.sun.org.apache.bcel.internal.generic.ANEWARRAY;
|
|
31 |
import com.sun.org.apache.bcel.internal.generic.BasicType;
|
|
32 |
import com.sun.org.apache.bcel.internal.generic.CHECKCAST;
|
|
33 |
import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
|
|
34 |
import com.sun.org.apache.bcel.internal.generic.DUP_X1;
|
|
35 |
import com.sun.org.apache.bcel.internal.generic.GETFIELD;
|
|
36 |
import com.sun.org.apache.bcel.internal.generic.ICONST;
|
|
37 |
import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
|
|
38 |
import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
|
|
39 |
import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
|
|
40 |
import com.sun.org.apache.bcel.internal.generic.InstructionList;
|
|
41 |
import com.sun.org.apache.bcel.internal.generic.NEW;
|
|
42 |
import com.sun.org.apache.bcel.internal.generic.NEWARRAY;
|
|
43 |
import com.sun.org.apache.bcel.internal.generic.PUSH;
|
|
44 |
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
|
|
45 |
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
|
|
46 |
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
|
|
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.DOM;
|
|
50 |
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
|
|
51 |
|
|
52 |
import org.xml.sax.Attributes;
|
|
53 |
import org.xml.sax.helpers.AttributesImpl;
|
|
54 |
|
|
55 |
|
|
56 |
/**
|
|
57 |
* @author Jacek Ambroziak
|
|
58 |
* @author Santiago Pericas-Geertsen
|
|
59 |
* @author G. Todd Miller
|
|
60 |
* @author Morten Jorensen
|
|
61 |
* @author Erwin Bolwidt <ejb@klomp.org>
|
|
62 |
* @author John Howard <JohnH@schemasoft.com>
|
|
63 |
*/
|
|
64 |
public abstract class SyntaxTreeNode implements Constants {
|
|
65 |
|
|
66 |
// Reference to the AST parser
|
|
67 |
private Parser _parser;
|
|
68 |
|
|
69 |
// AST navigation pointers
|
|
70 |
protected SyntaxTreeNode _parent; // Parent node
|
|
71 |
private Stylesheet _stylesheet; // Stylesheet ancestor node
|
|
72 |
private Template _template; // Template ancestor node
|
|
73 |
private final Vector _contents = new Vector(2); // Child nodes
|
|
74 |
|
|
75 |
// Element description data
|
|
76 |
protected QName _qname; // The element QName
|
|
77 |
private int _line; // Source file line number
|
|
78 |
protected AttributesImpl _attributes = null; // Attributes of this element
|
|
79 |
private Hashtable _prefixMapping = null; // Namespace declarations
|
|
80 |
|
|
81 |
// Sentinel - used to denote unrecognised syntaxt tree nodes.
|
|
82 |
protected static final SyntaxTreeNode Dummy = new AbsolutePathPattern(null);
|
|
83 |
|
|
84 |
// These two are used for indenting nodes in the AST (debug output)
|
|
85 |
protected static final int IndentIncrement = 4;
|
|
86 |
private static final char[] _spaces =
|
|
87 |
" ".toCharArray();
|
|
88 |
|
|
89 |
/**
|
|
90 |
* Creates a new SyntaxTreeNode with a 'null' QName and no source file
|
|
91 |
* line number reference.
|
|
92 |
*/
|
|
93 |
public SyntaxTreeNode() {
|
|
94 |
_line = 0;
|
|
95 |
_qname = null;
|
|
96 |
}
|
|
97 |
|
|
98 |
/**
|
|
99 |
* Creates a new SyntaxTreeNode with a 'null' QName.
|
|
100 |
* @param line Source file line number reference
|
|
101 |
*/
|
|
102 |
public SyntaxTreeNode(int line) {
|
|
103 |
_line = line;
|
|
104 |
_qname = null;
|
|
105 |
}
|
|
106 |
|
|
107 |
/**
|
|
108 |
* Creates a new SyntaxTreeNode with no source file line number reference.
|
|
109 |
* @param uri The element's namespace URI
|
|
110 |
* @param prefix The element's namespace prefix
|
|
111 |
* @param local The element's local name
|
|
112 |
*/
|
|
113 |
public SyntaxTreeNode(String uri, String prefix, String local) {
|
|
114 |
_line = 0;
|
|
115 |
setQName(uri, prefix, local);
|
|
116 |
}
|
|
117 |
|
|
118 |
/**
|
|
119 |
* Set the source file line number for this element
|
|
120 |
* @param line The source file line number.
|
|
121 |
*/
|
|
122 |
protected final void setLineNumber(int line) {
|
|
123 |
_line = line;
|
|
124 |
}
|
|
125 |
|
|
126 |
/**
|
|
127 |
* Get the source file line number for this element. If unavailable, lookup
|
|
128 |
* in ancestors.
|
|
129 |
*
|
|
130 |
* @return The source file line number.
|
|
131 |
*/
|
|
132 |
public final int getLineNumber() {
|
|
133 |
if (_line > 0) return _line;
|
|
134 |
SyntaxTreeNode parent = getParent();
|
|
135 |
return (parent != null) ? parent.getLineNumber() : 0;
|
|
136 |
}
|
|
137 |
|
|
138 |
/**
|
|
139 |
* Set the QName for the syntax tree node.
|
|
140 |
* @param qname The QName for the syntax tree node
|
|
141 |
*/
|
|
142 |
protected void setQName(QName qname) {
|
|
143 |
_qname = qname;
|
|
144 |
}
|
|
145 |
|
|
146 |
/**
|
|
147 |
* Set the QName for the SyntaxTreeNode
|
|
148 |
* @param uri The element's namespace URI
|
|
149 |
* @param prefix The element's namespace prefix
|
|
150 |
* @param local The element's local name
|
|
151 |
*/
|
|
152 |
protected void setQName(String uri, String prefix, String localname) {
|
|
153 |
_qname = new QName(uri, prefix, localname);
|
|
154 |
}
|
|
155 |
|
|
156 |
/**
|
|
157 |
* Set the QName for the SyntaxTreeNode
|
|
158 |
* @param qname The QName for the syntax tree node
|
|
159 |
*/
|
|
160 |
protected QName getQName() {
|
|
161 |
return(_qname);
|
|
162 |
}
|
|
163 |
|
|
164 |
/**
|
|
165 |
* Set the attributes for this SyntaxTreeNode.
|
|
166 |
* @param attributes Attributes for the element. Must be passed in as an
|
|
167 |
* implementation of org.xml.sax.Attributes.
|
|
168 |
*/
|
|
169 |
protected void setAttributes(AttributesImpl attributes) {
|
|
170 |
_attributes = attributes;
|
|
171 |
}
|
|
172 |
|
|
173 |
/**
|
|
174 |
* Returns a value for an attribute from the source element.
|
|
175 |
* @param qname The QName of the attribute to return.
|
|
176 |
* @return The value of the attribute of name 'qname'.
|
|
177 |
*/
|
|
178 |
protected String getAttribute(String qname) {
|
|
179 |
if (_attributes == null) {
|
|
180 |
return EMPTYSTRING;
|
|
181 |
}
|
|
182 |
final String value = _attributes.getValue(qname);
|
|
183 |
return (value == null || value.equals(EMPTYSTRING)) ?
|
|
184 |
EMPTYSTRING : value;
|
|
185 |
}
|
|
186 |
|
|
187 |
protected String getAttribute(String prefix, String localName) {
|
|
188 |
return getAttribute(prefix + ':' + localName);
|
|
189 |
}
|
|
190 |
|
|
191 |
protected boolean hasAttribute(String qname) {
|
|
192 |
return (_attributes != null && _attributes.getValue(qname) != null);
|
|
193 |
}
|
|
194 |
|
|
195 |
protected void addAttribute(String qname, String value) {
|
|
196 |
int index = _attributes.getIndex(qname);
|
|
197 |
if (index != -1) {
|
|
198 |
_attributes.setAttribute(index, "", Util.getLocalName(qname),
|
|
199 |
qname, "CDATA", value);
|
|
200 |
}
|
|
201 |
else {
|
|
202 |
_attributes.addAttribute("", Util.getLocalName(qname), qname,
|
|
203 |
"CDATA", value);
|
|
204 |
}
|
|
205 |
}
|
|
206 |
|
|
207 |
/**
|
|
208 |
* Returns a list of all attributes declared for the element represented by
|
|
209 |
* this syntax tree node.
|
|
210 |
* @return Attributes for this syntax tree node
|
|
211 |
*/
|
|
212 |
protected Attributes getAttributes() {
|
|
213 |
return(_attributes);
|
|
214 |
}
|
|
215 |
|
|
216 |
/**
|
|
217 |
* Sets the prefix mapping for the namespaces that were declared in this
|
|
218 |
* element. This does not include all prefix mappings in scope, so one
|
|
219 |
* may have to check ancestor elements to get all mappings that are in
|
|
220 |
* in scope. The prefixes must be passed in as a Hashtable that maps
|
|
221 |
* namespace prefixes (String objects) to namespace URIs (also String).
|
|
222 |
* @param mapping The Hashtable containing the mappings.
|
|
223 |
*/
|
|
224 |
protected void setPrefixMapping(Hashtable mapping) {
|
|
225 |
_prefixMapping = mapping;
|
|
226 |
}
|
|
227 |
|
|
228 |
/**
|
|
229 |
* Returns a Hashtable containing the prefix mappings that were declared
|
|
230 |
* for this element. This does not include all prefix mappings in scope,
|
|
231 |
* so one may have to check ancestor elements to get all mappings that are
|
|
232 |
* in in scope.
|
|
233 |
* @return Prefix mappings (for this element only).
|
|
234 |
*/
|
|
235 |
protected Hashtable getPrefixMapping() {
|
|
236 |
return _prefixMapping;
|
|
237 |
}
|
|
238 |
|
|
239 |
/**
|
|
240 |
* Adds a single prefix mapping to this syntax tree node.
|
|
241 |
* @param prefix Namespace prefix.
|
|
242 |
* @param uri Namespace URI.
|
|
243 |
*/
|
|
244 |
protected void addPrefixMapping(String prefix, String uri) {
|
|
245 |
if (_prefixMapping == null)
|
|
246 |
_prefixMapping = new Hashtable();
|
|
247 |
_prefixMapping.put(prefix, uri);
|
|
248 |
}
|
|
249 |
|
|
250 |
/**
|
|
251 |
* Returns any namespace URI that is in scope for a given prefix. This
|
|
252 |
* method checks namespace mappings for this element, and if necessary
|
|
253 |
* for ancestor elements as well (ie. if the prefix maps to an URI in this
|
|
254 |
* scope then you'll definately get the URI from this method).
|
|
255 |
* @param prefix Namespace prefix.
|
|
256 |
* @return Namespace URI.
|
|
257 |
*/
|
|
258 |
protected String lookupNamespace(String prefix) {
|
|
259 |
// Initialise the output (default is 'null' for undefined)
|
|
260 |
String uri = null;
|
|
261 |
|
|
262 |
// First look up the prefix/uri mapping in our own hashtable...
|
|
263 |
if (_prefixMapping != null)
|
|
264 |
uri = (String)_prefixMapping.get(prefix);
|
|
265 |
// ... but if we can't find it there we ask our parent for the mapping
|
|
266 |
if ((uri == null) && (_parent != null)) {
|
|
267 |
uri = _parent.lookupNamespace(prefix);
|
|
268 |
if ((prefix == Constants.EMPTYSTRING) && (uri == null))
|
|
269 |
uri = Constants.EMPTYSTRING;
|
|
270 |
}
|
|
271 |
// ... and then we return whatever URI we've got.
|
|
272 |
return(uri);
|
|
273 |
}
|
|
274 |
|
|
275 |
/**
|
|
276 |
* Returns any namespace prefix that is mapped to a prefix in the current
|
|
277 |
* scope. This method checks namespace mappings for this element, and if
|
|
278 |
* necessary for ancestor elements as well (ie. if the URI is declared
|
|
279 |
* within the current scope then you'll definately get the prefix from
|
|
280 |
* this method). Note that this is a very slow method and consequentially
|
|
281 |
* it should only be used strictly when needed.
|
|
282 |
* @param uri Namespace URI.
|
|
283 |
* @return Namespace prefix.
|
|
284 |
*/
|
|
285 |
protected String lookupPrefix(String uri) {
|
|
286 |
// Initialise the output (default is 'null' for undefined)
|
|
287 |
String prefix = null;
|
|
288 |
|
|
289 |
// First look up the prefix/uri mapping in our own hashtable...
|
|
290 |
if ((_prefixMapping != null) &&
|
|
291 |
(_prefixMapping.contains(uri))) {
|
|
292 |
Enumeration prefixes = _prefixMapping.keys();
|
|
293 |
while (prefixes.hasMoreElements()) {
|
|
294 |
prefix = (String)prefixes.nextElement();
|
|
295 |
String mapsTo = (String)_prefixMapping.get(prefix);
|
|
296 |
if (mapsTo.equals(uri)) return(prefix);
|
|
297 |
}
|
|
298 |
}
|
|
299 |
// ... but if we can't find it there we ask our parent for the mapping
|
|
300 |
else if (_parent != null) {
|
|
301 |
prefix = _parent.lookupPrefix(uri);
|
|
302 |
if ((uri == Constants.EMPTYSTRING) && (prefix == null))
|
|
303 |
prefix = Constants.EMPTYSTRING;
|
|
304 |
}
|
|
305 |
return(prefix);
|
|
306 |
}
|
|
307 |
|
|
308 |
/**
|
|
309 |
* Set this node's parser. The parser (the XSLT parser) gives this
|
|
310 |
* syntax tree node access to the symbol table and XPath parser.
|
|
311 |
* @param parser The XSLT parser.
|
|
312 |
*/
|
|
313 |
protected void setParser(Parser parser) {
|
|
314 |
_parser = parser;
|
|
315 |
}
|
|
316 |
|
|
317 |
/**
|
|
318 |
* Returns this node's XSLT parser.
|
|
319 |
* @return The XSLT parser.
|
|
320 |
*/
|
|
321 |
public final Parser getParser() {
|
|
322 |
return _parser;
|
|
323 |
}
|
|
324 |
|
|
325 |
/**
|
|
326 |
* Set this syntax tree node's parent node, if unset. For
|
|
327 |
* re-parenting just use <code>node._parent = newparent</code>.
|
|
328 |
*
|
|
329 |
* @param parent The parent node.
|
|
330 |
*/
|
|
331 |
protected void setParent(SyntaxTreeNode parent) {
|
|
332 |
if (_parent == null) _parent = parent;
|
|
333 |
}
|
|
334 |
|
|
335 |
/**
|
|
336 |
* Returns this syntax tree node's parent node.
|
|
337 |
* @return The parent syntax tree node.
|
|
338 |
*/
|
|
339 |
protected final SyntaxTreeNode getParent() {
|
|
340 |
return _parent;
|
|
341 |
}
|
|
342 |
|
|
343 |
/**
|
|
344 |
* Returns 'true' if this syntax tree node is the Sentinal node.
|
|
345 |
* @return 'true' if this syntax tree node is the Sentinal node.
|
|
346 |
*/
|
|
347 |
protected final boolean isDummy() {
|
|
348 |
return this == Dummy;
|
|
349 |
}
|
|
350 |
|
|
351 |
/**
|
|
352 |
* Get the import precedence of this element. The import precedence equals
|
|
353 |
* the import precedence of the stylesheet in which this element occured.
|
|
354 |
* @return The import precedence of this syntax tree node.
|
|
355 |
*/
|
|
356 |
protected int getImportPrecedence() {
|
|
357 |
Stylesheet stylesheet = getStylesheet();
|
|
358 |
if (stylesheet == null) return Integer.MIN_VALUE;
|
|
359 |
return stylesheet.getImportPrecedence();
|
|
360 |
}
|
|
361 |
|
|
362 |
/**
|
|
363 |
* Get the Stylesheet node that represents the <xsl:stylesheet/> element
|
|
364 |
* that this node occured under.
|
|
365 |
* @return The Stylesheet ancestor node of this node.
|
|
366 |
*/
|
|
367 |
public Stylesheet getStylesheet() {
|
|
368 |
if (_stylesheet == null) {
|
|
369 |
SyntaxTreeNode parent = this;
|
|
370 |
while (parent != null) {
|
|
371 |
if (parent instanceof Stylesheet)
|
|
372 |
return((Stylesheet)parent);
|
|
373 |
parent = parent.getParent();
|
|
374 |
}
|
|
375 |
_stylesheet = (Stylesheet)parent;
|
|
376 |
}
|
|
377 |
return(_stylesheet);
|
|
378 |
}
|
|
379 |
|
|
380 |
/**
|
|
381 |
* Get the Template node that represents the <xsl:template/> element
|
|
382 |
* that this node occured under. Note that this method will return 'null'
|
|
383 |
* for nodes that represent top-level elements.
|
|
384 |
* @return The Template ancestor node of this node or 'null'.
|
|
385 |
*/
|
|
386 |
protected Template getTemplate() {
|
|
387 |
if (_template == null) {
|
|
388 |
SyntaxTreeNode parent = this;
|
|
389 |
while ((parent != null) && (!(parent instanceof Template)))
|
|
390 |
parent = parent.getParent();
|
|
391 |
_template = (Template)parent;
|
|
392 |
}
|
|
393 |
return(_template);
|
|
394 |
}
|
|
395 |
|
|
396 |
/**
|
|
397 |
* Returns a reference to the XSLTC (XSLT compiler) in use.
|
|
398 |
* @return XSLTC - XSLT compiler.
|
|
399 |
*/
|
|
400 |
protected final XSLTC getXSLTC() {
|
|
401 |
return _parser.getXSLTC();
|
|
402 |
}
|
|
403 |
|
|
404 |
/**
|
|
405 |
* Returns the XSLT parser's symbol table.
|
|
406 |
* @return Symbol table.
|
|
407 |
*/
|
|
408 |
protected final SymbolTable getSymbolTable() {
|
|
409 |
return (_parser == null) ? null : _parser.getSymbolTable();
|
|
410 |
}
|
|
411 |
|
|
412 |
/**
|
|
413 |
* Parse the contents of this syntax tree nodes (child nodes, XPath
|
|
414 |
* expressions, patterns and functions). The default behaviour is to parser
|
|
415 |
* the syntax tree node's children (since there are no common expressions,
|
|
416 |
* patterns, etc. that can be handled in this base class.
|
|
417 |
* @param parser reference to the XSLT parser
|
|
418 |
*/
|
|
419 |
public void parseContents(Parser parser) {
|
|
420 |
parseChildren(parser);
|
|
421 |
}
|
|
422 |
|
|
423 |
/**
|
|
424 |
* Parse all children of this syntax tree node. This method is normally
|
|
425 |
* called by the parseContents() method.
|
|
426 |
* @param parser reference to the XSLT parser
|
|
427 |
*/
|
|
428 |
protected final void parseChildren(Parser parser) {
|
|
429 |
|
|
430 |
Vector locals = null; // only create when needed
|
|
431 |
|
|
432 |
final int count = _contents.size();
|
|
433 |
for (int i=0; i<count; i++) {
|
|
434 |
SyntaxTreeNode child = (SyntaxTreeNode)_contents.elementAt(i);
|
|
435 |
parser.getSymbolTable().setCurrentNode(child);
|
|
436 |
child.parseContents(parser);
|
|
437 |
// if variable or parameter, add it to scope
|
|
438 |
final QName varOrParamName = updateScope(parser, child);
|
|
439 |
if (varOrParamName != null) {
|
|
440 |
if (locals == null) {
|
|
441 |
locals = new Vector(2);
|
|
442 |
}
|
|
443 |
locals.addElement(varOrParamName);
|
|
444 |
}
|
|
445 |
}
|
|
446 |
|
|
447 |
parser.getSymbolTable().setCurrentNode(this);
|
|
448 |
|
|
449 |
// after the last element, remove any locals from scope
|
|
450 |
if (locals != null) {
|
|
451 |
final int nLocals = locals.size();
|
|
452 |
for (int i = 0; i < nLocals; i++) {
|
|
453 |
parser.removeVariable((QName)locals.elementAt(i));
|
|
454 |
}
|
|
455 |
}
|
|
456 |
}
|
|
457 |
|
|
458 |
/**
|
|
459 |
* Add a node to the current scope and return name of a variable or
|
|
460 |
* parameter if the node represents a variable or a parameter.
|
|
461 |
*/
|
|
462 |
protected QName updateScope(Parser parser, SyntaxTreeNode node) {
|
|
463 |
if (node instanceof Variable) {
|
|
464 |
final Variable var = (Variable)node;
|
|
465 |
parser.addVariable(var);
|
|
466 |
return var.getName();
|
|
467 |
}
|
|
468 |
else if (node instanceof Param) {
|
|
469 |
final Param param = (Param)node;
|
|
470 |
parser.addParameter(param);
|
|
471 |
return param.getName();
|
|
472 |
}
|
|
473 |
else {
|
|
474 |
return null;
|
|
475 |
}
|
|
476 |
}
|
|
477 |
|
|
478 |
/**
|
|
479 |
* Type check the children of this node. The type check phase may add
|
|
480 |
* coercions (CastExpr) to the AST.
|
|
481 |
* @param stable The compiler/parser's symbol table
|
|
482 |
*/
|
|
483 |
public abstract Type typeCheck(SymbolTable stable) throws TypeCheckError;
|
|
484 |
|
|
485 |
/**
|
|
486 |
* Call typeCheck() on all child syntax tree nodes.
|
|
487 |
* @param stable The compiler/parser's symbol table
|
|
488 |
*/
|
|
489 |
protected Type typeCheckContents(SymbolTable stable) throws TypeCheckError {
|
|
490 |
final int n = elementCount();
|
|
491 |
for (int i = 0; i < n; i++) {
|
|
492 |
SyntaxTreeNode item = (SyntaxTreeNode)_contents.elementAt(i);
|
|
493 |
item.typeCheck(stable);
|
|
494 |
}
|
|
495 |
return Type.Void;
|
|
496 |
}
|
|
497 |
|
|
498 |
/**
|
|
499 |
* Translate this abstract syntax tree node into JVM bytecodes.
|
|
500 |
* @param classGen BCEL Java class generator
|
|
501 |
* @param methodGen BCEL Java method generator
|
|
502 |
*/
|
|
503 |
public abstract void translate(ClassGenerator classGen,
|
|
504 |
MethodGenerator methodGen);
|
|
505 |
|
|
506 |
/**
|
|
507 |
* Call translate() on all child syntax tree nodes.
|
|
508 |
* @param classGen BCEL Java class generator
|
|
509 |
* @param methodGen BCEL Java method generator
|
|
510 |
*/
|
|
511 |
protected void translateContents(ClassGenerator classGen,
|
|
512 |
MethodGenerator methodGen) {
|
|
513 |
// Call translate() on all child nodes
|
|
514 |
final int n = elementCount();
|
12458
|
515 |
|
6
|
516 |
for (int i = 0; i < n; i++) {
|
12458
|
517 |
methodGen.markChunkStart();
|
6
|
518 |
final SyntaxTreeNode item = (SyntaxTreeNode)_contents.elementAt(i);
|
|
519 |
item.translate(classGen, methodGen);
|
12458
|
520 |
methodGen.markChunkEnd();
|
6
|
521 |
}
|
|
522 |
|
|
523 |
// After translation, unmap any registers for any variables/parameters
|
|
524 |
// that were declared in this scope. Performing this unmapping in the
|
|
525 |
// same AST scope as the declaration deals with the problems of
|
|
526 |
// references falling out-of-scope inside the for-each element.
|
|
527 |
// (the cause of which being 'lazy' register allocation for references)
|
|
528 |
for (int i = 0; i < n; i++) {
|
|
529 |
if( _contents.elementAt(i) instanceof VariableBase) {
|
|
530 |
final VariableBase var = (VariableBase)_contents.elementAt(i);
|
|
531 |
var.unmapRegister(methodGen);
|
|
532 |
}
|
|
533 |
}
|
|
534 |
}
|
|
535 |
|
|
536 |
/**
|
|
537 |
* Return true if the node represents a simple RTF.
|
|
538 |
*
|
|
539 |
* A node is a simple RTF if all children only produce Text value.
|
|
540 |
*
|
|
541 |
* @param node A node
|
|
542 |
* @return true if the node content can be considered as a simple RTF.
|
|
543 |
*/
|
|
544 |
private boolean isSimpleRTF(SyntaxTreeNode node) {
|
|
545 |
|
|
546 |
Vector contents = node.getContents();
|
|
547 |
for (int i = 0; i < contents.size(); i++) {
|
|
548 |
SyntaxTreeNode item = (SyntaxTreeNode)contents.elementAt(i);
|
|
549 |
if (!isTextElement(item, false))
|
|
550 |
return false;
|
|
551 |
}
|
|
552 |
|
|
553 |
return true;
|
|
554 |
}
|
|
555 |
|
|
556 |
/**
|
|
557 |
* Return true if the node represents an adaptive RTF.
|
|
558 |
*
|
|
559 |
* A node is an adaptive RTF if each children is a Text element
|
|
560 |
* or it is <xsl:call-template> or <xsl:apply-templates>.
|
|
561 |
*
|
|
562 |
* @param node A node
|
|
563 |
* @return true if the node content can be considered as an adaptive RTF.
|
|
564 |
*/
|
|
565 |
private boolean isAdaptiveRTF(SyntaxTreeNode node) {
|
|
566 |
|
|
567 |
Vector contents = node.getContents();
|
|
568 |
for (int i = 0; i < contents.size(); i++) {
|
|
569 |
SyntaxTreeNode item = (SyntaxTreeNode)contents.elementAt(i);
|
|
570 |
if (!isTextElement(item, true))
|
|
571 |
return false;
|
|
572 |
}
|
|
573 |
|
|
574 |
return true;
|
|
575 |
}
|
|
576 |
|
|
577 |
/**
|
|
578 |
* Return true if the node only produces Text content.
|
|
579 |
*
|
|
580 |
* A node is a Text element if it is Text, xsl:value-of, xsl:number,
|
|
581 |
* or a combination of these nested in a control instruction (xsl:if or
|
|
582 |
* xsl:choose).
|
|
583 |
*
|
|
584 |
* If the doExtendedCheck flag is true, xsl:call-template and xsl:apply-templates
|
|
585 |
* are also considered as Text elements.
|
|
586 |
*
|
|
587 |
* @param node A node
|
|
588 |
* @param doExtendedCheck If this flag is true, <xsl:call-template> and
|
|
589 |
* <xsl:apply-templates> are also considered as Text elements.
|
|
590 |
*
|
|
591 |
* @return true if the node of Text type
|
|
592 |
*/
|
|
593 |
private boolean isTextElement(SyntaxTreeNode node, boolean doExtendedCheck) {
|
|
594 |
if (node instanceof ValueOf || node instanceof Number
|
|
595 |
|| node instanceof Text)
|
|
596 |
{
|
|
597 |
return true;
|
|
598 |
}
|
|
599 |
else if (node instanceof If) {
|
|
600 |
return doExtendedCheck ? isAdaptiveRTF(node) : isSimpleRTF(node);
|
|
601 |
}
|
|
602 |
else if (node instanceof Choose) {
|
|
603 |
Vector contents = node.getContents();
|
|
604 |
for (int i = 0; i < contents.size(); i++) {
|
|
605 |
SyntaxTreeNode item = (SyntaxTreeNode)contents.elementAt(i);
|
|
606 |
if (item instanceof Text ||
|
|
607 |
((item instanceof When || item instanceof Otherwise)
|
|
608 |
&& ((doExtendedCheck && isAdaptiveRTF(item))
|
|
609 |
|| (!doExtendedCheck && isSimpleRTF(item)))))
|
|
610 |
continue;
|
|
611 |
else
|
|
612 |
return false;
|
|
613 |
}
|
|
614 |
return true;
|
|
615 |
}
|
|
616 |
else if (doExtendedCheck &&
|
|
617 |
(node instanceof CallTemplate
|
|
618 |
|| node instanceof ApplyTemplates))
|
|
619 |
return true;
|
|
620 |
else
|
|
621 |
return false;
|
|
622 |
}
|
|
623 |
|
|
624 |
/**
|
|
625 |
* Utility method used by parameters and variables to store result trees
|
|
626 |
* @param classGen BCEL Java class generator
|
|
627 |
* @param methodGen BCEL Java method generator
|
|
628 |
*/
|
|
629 |
protected void compileResultTree(ClassGenerator classGen,
|
|
630 |
MethodGenerator methodGen)
|
|
631 |
{
|
|
632 |
final ConstantPoolGen cpg = classGen.getConstantPool();
|
|
633 |
final InstructionList il = methodGen.getInstructionList();
|
|
634 |
final Stylesheet stylesheet = classGen.getStylesheet();
|
|
635 |
|
|
636 |
boolean isSimple = isSimpleRTF(this);
|
|
637 |
boolean isAdaptive = false;
|
|
638 |
if (!isSimple) {
|
|
639 |
isAdaptive = isAdaptiveRTF(this);
|
|
640 |
}
|
|
641 |
|
|
642 |
int rtfType = isSimple ? DOM.SIMPLE_RTF
|
|
643 |
: (isAdaptive ? DOM.ADAPTIVE_RTF : DOM.TREE_RTF);
|
|
644 |
|
|
645 |
// Save the current handler base on the stack
|
|
646 |
il.append(methodGen.loadHandler());
|
|
647 |
|
|
648 |
final String DOM_CLASS = classGen.getDOMClass();
|
|
649 |
|
|
650 |
// Create new instance of DOM class (with RTF_INITIAL_SIZE nodes)
|
|
651 |
//int index = cpg.addMethodref(DOM_IMPL, "<init>", "(I)V");
|
|
652 |
//il.append(new NEW(cpg.addClass(DOM_IMPL)));
|
|
653 |
|
|
654 |
il.append(methodGen.loadDOM());
|
|
655 |
int index = cpg.addInterfaceMethodref(DOM_INTF,
|
|
656 |
"getResultTreeFrag",
|
|
657 |
"(IIZ)" + DOM_INTF_SIG);
|
|
658 |
il.append(new PUSH(cpg, RTF_INITIAL_SIZE));
|
|
659 |
il.append(new PUSH(cpg, rtfType));
|
|
660 |
il.append(new PUSH(cpg, stylesheet.callsNodeset()));
|
|
661 |
il.append(new INVOKEINTERFACE(index,4));
|
|
662 |
|
|
663 |
il.append(DUP);
|
|
664 |
|
|
665 |
// Overwrite old handler with DOM handler
|
|
666 |
index = cpg.addInterfaceMethodref(DOM_INTF,
|
|
667 |
"getOutputDomBuilder",
|
|
668 |
"()" + TRANSLET_OUTPUT_SIG);
|
|
669 |
|
|
670 |
il.append(new INVOKEINTERFACE(index,1));
|
|
671 |
il.append(DUP);
|
|
672 |
il.append(methodGen.storeHandler());
|
|
673 |
|
|
674 |
// Call startDocument on the new handler
|
|
675 |
il.append(methodGen.startDocument());
|
|
676 |
|
|
677 |
// Instantiate result tree fragment
|
|
678 |
translateContents(classGen, methodGen);
|
|
679 |
|
|
680 |
// Call endDocument on the new handler
|
|
681 |
il.append(methodGen.loadHandler());
|
|
682 |
il.append(methodGen.endDocument());
|
|
683 |
|
|
684 |
// Check if we need to wrap the DOMImpl object in a DOMAdapter object.
|
|
685 |
// DOMAdapter is not needed if the RTF is a simple RTF and the nodeset()
|
|
686 |
// function is not used.
|
|
687 |
if (stylesheet.callsNodeset()
|
|
688 |
&& !DOM_CLASS.equals(DOM_IMPL_CLASS)) {
|
|
689 |
// new com.sun.org.apache.xalan.internal.xsltc.dom.DOMAdapter(DOMImpl,String[]);
|
|
690 |
index = cpg.addMethodref(DOM_ADAPTER_CLASS,
|
|
691 |
"<init>",
|
|
692 |
"("+DOM_INTF_SIG+
|
|
693 |
"["+STRING_SIG+
|
|
694 |
"["+STRING_SIG+
|
|
695 |
"[I"+
|
|
696 |
"["+STRING_SIG+")V");
|
|
697 |
il.append(new NEW(cpg.addClass(DOM_ADAPTER_CLASS)));
|
|
698 |
il.append(new DUP_X1());
|
|
699 |
il.append(SWAP);
|
|
700 |
|
|
701 |
/*
|
|
702 |
* Give the DOM adapter an empty type mapping if the nodeset
|
|
703 |
* extension function is never called.
|
|
704 |
*/
|
|
705 |
if (!stylesheet.callsNodeset()) {
|
|
706 |
il.append(new ICONST(0));
|
|
707 |
il.append(new ANEWARRAY(cpg.addClass(STRING)));
|
|
708 |
il.append(DUP);
|
|
709 |
il.append(DUP);
|
|
710 |
il.append(new ICONST(0));
|
|
711 |
il.append(new NEWARRAY(BasicType.INT));
|
|
712 |
il.append(SWAP);
|
|
713 |
il.append(new INVOKESPECIAL(index));
|
|
714 |
}
|
|
715 |
else {
|
|
716 |
// Push name arrays on the stack
|
|
717 |
il.append(ALOAD_0);
|
|
718 |
il.append(new GETFIELD(cpg.addFieldref(TRANSLET_CLASS,
|
|
719 |
NAMES_INDEX,
|
|
720 |
NAMES_INDEX_SIG)));
|
|
721 |
il.append(ALOAD_0);
|
|
722 |
il.append(new GETFIELD(cpg.addFieldref(TRANSLET_CLASS,
|
|
723 |
URIS_INDEX,
|
|
724 |
URIS_INDEX_SIG)));
|
|
725 |
il.append(ALOAD_0);
|
|
726 |
il.append(new GETFIELD(cpg.addFieldref(TRANSLET_CLASS,
|
|
727 |
TYPES_INDEX,
|
|
728 |
TYPES_INDEX_SIG)));
|
|
729 |
il.append(ALOAD_0);
|
|
730 |
il.append(new GETFIELD(cpg.addFieldref(TRANSLET_CLASS,
|
|
731 |
NAMESPACE_INDEX,
|
|
732 |
NAMESPACE_INDEX_SIG)));
|
|
733 |
|
|
734 |
// Initialized DOM adapter
|
|
735 |
il.append(new INVOKESPECIAL(index));
|
|
736 |
|
|
737 |
// Add DOM adapter to MultiDOM class by calling addDOMAdapter()
|
|
738 |
il.append(DUP);
|
|
739 |
il.append(methodGen.loadDOM());
|
|
740 |
il.append(new CHECKCAST(cpg.addClass(classGen.getDOMClass())));
|
|
741 |
il.append(SWAP);
|
|
742 |
index = cpg.addMethodref(MULTI_DOM_CLASS,
|
|
743 |
"addDOMAdapter",
|
|
744 |
"(" + DOM_ADAPTER_SIG + ")I");
|
|
745 |
il.append(new INVOKEVIRTUAL(index));
|
|
746 |
il.append(POP); // ignore mask returned by addDOMAdapter
|
|
747 |
}
|
|
748 |
}
|
|
749 |
|
|
750 |
// Restore old handler base from stack
|
|
751 |
il.append(SWAP);
|
|
752 |
il.append(methodGen.storeHandler());
|
|
753 |
}
|
|
754 |
|
|
755 |
/**
|
|
756 |
* Returns true if this expression/instruction depends on the context. By
|
|
757 |
* default, every expression/instruction depends on the context unless it
|
|
758 |
* overrides this method. Currently used to determine if result trees are
|
|
759 |
* compiled using procedures or little DOMs (result tree fragments).
|
|
760 |
* @return 'true' if this node depends on the context.
|
|
761 |
*/
|
|
762 |
protected boolean contextDependent() {
|
|
763 |
return true;
|
|
764 |
}
|
|
765 |
|
|
766 |
/**
|
|
767 |
* Return true if any of the expressions/instructions in the contents of
|
|
768 |
* this node is context dependent.
|
|
769 |
* @return 'true' if the contents of this node is context dependent.
|
|
770 |
*/
|
|
771 |
protected boolean dependentContents() {
|
|
772 |
final int n = elementCount();
|
|
773 |
for (int i = 0; i < n; i++) {
|
|
774 |
final SyntaxTreeNode item = (SyntaxTreeNode)_contents.elementAt(i);
|
|
775 |
if (item.contextDependent()) {
|
|
776 |
return true;
|
|
777 |
}
|
|
778 |
}
|
|
779 |
return false;
|
|
780 |
}
|
|
781 |
|
|
782 |
/**
|
|
783 |
* Adds a child node to this syntax tree node.
|
|
784 |
* @param element is the new child node.
|
|
785 |
*/
|
|
786 |
protected final void addElement(SyntaxTreeNode element) {
|
|
787 |
_contents.addElement(element);
|
|
788 |
element.setParent(this);
|
|
789 |
}
|
|
790 |
|
|
791 |
/**
|
|
792 |
* Inserts the first child node of this syntax tree node. The existing
|
|
793 |
* children are shifted back one position.
|
|
794 |
* @param element is the new child node.
|
|
795 |
*/
|
|
796 |
protected final void setFirstElement(SyntaxTreeNode element) {
|
|
797 |
_contents.insertElementAt(element,0);
|
|
798 |
element.setParent(this);
|
|
799 |
}
|
|
800 |
|
|
801 |
/**
|
|
802 |
* Removed a child node of this syntax tree node.
|
|
803 |
* @param element is the child node to remove.
|
|
804 |
*/
|
|
805 |
protected final void removeElement(SyntaxTreeNode element) {
|
|
806 |
_contents.remove(element);
|
|
807 |
element.setParent(null);
|
|
808 |
}
|
|
809 |
|
|
810 |
/**
|
|
811 |
* Returns a Vector containing all the child nodes of this node.
|
|
812 |
* @return A Vector containing all the child nodes of this node.
|
|
813 |
*/
|
|
814 |
protected final Vector getContents() {
|
|
815 |
return _contents;
|
|
816 |
}
|
|
817 |
|
|
818 |
/**
|
|
819 |
* Tells you if this node has any child nodes.
|
|
820 |
* @return 'true' if this node has any children.
|
|
821 |
*/
|
|
822 |
protected final boolean hasContents() {
|
|
823 |
return elementCount() > 0;
|
|
824 |
}
|
|
825 |
|
|
826 |
/**
|
|
827 |
* Returns the number of children this node has.
|
|
828 |
* @return Number of child nodes.
|
|
829 |
*/
|
|
830 |
protected final int elementCount() {
|
|
831 |
return _contents.size();
|
|
832 |
}
|
|
833 |
|
|
834 |
/**
|
|
835 |
* Returns an Enumeration of all child nodes of this node.
|
|
836 |
* @return An Enumeration of all child nodes of this node.
|
|
837 |
*/
|
|
838 |
protected final Enumeration elements() {
|
|
839 |
return _contents.elements();
|
|
840 |
}
|
|
841 |
|
|
842 |
/**
|
|
843 |
* Returns a child node at a given position.
|
|
844 |
* @param pos The child node's position.
|
|
845 |
* @return The child node.
|
|
846 |
*/
|
|
847 |
protected final Object elementAt(int pos) {
|
|
848 |
return _contents.elementAt(pos);
|
|
849 |
}
|
|
850 |
|
|
851 |
/**
|
|
852 |
* Returns this element's last child
|
|
853 |
* @return The child node.
|
|
854 |
*/
|
|
855 |
protected final SyntaxTreeNode lastChild() {
|
|
856 |
if (_contents.size() == 0) return null;
|
|
857 |
return (SyntaxTreeNode)_contents.lastElement();
|
|
858 |
}
|
|
859 |
|
|
860 |
/**
|
|
861 |
* Displays the contents of this syntax tree node (to stdout).
|
|
862 |
* This method is intended for debugging _only_, and should be overridden
|
|
863 |
* by all syntax tree node implementations.
|
|
864 |
* @param indent Indentation level for syntax tree levels.
|
|
865 |
*/
|
|
866 |
public void display(int indent) {
|
|
867 |
displayContents(indent);
|
|
868 |
}
|
|
869 |
|
|
870 |
/**
|
|
871 |
* Displays the contents of this syntax tree node (to stdout).
|
|
872 |
* This method is intended for debugging _only_ !!!
|
|
873 |
* @param indent Indentation level for syntax tree levels.
|
|
874 |
*/
|
|
875 |
protected void displayContents(int indent) {
|
|
876 |
final int n = elementCount();
|
|
877 |
for (int i = 0; i < n; i++) {
|
|
878 |
SyntaxTreeNode item = (SyntaxTreeNode)_contents.elementAt(i);
|
|
879 |
item.display(indent);
|
|
880 |
}
|
|
881 |
}
|
|
882 |
|
|
883 |
/**
|
|
884 |
* Set the indentation level for debug output.
|
|
885 |
* @param indent Indentation level for syntax tree levels.
|
|
886 |
*/
|
|
887 |
protected final void indent(int indent) {
|
|
888 |
System.out.print(new String(_spaces, 0, indent));
|
|
889 |
}
|
|
890 |
|
|
891 |
/**
|
|
892 |
* Report an error to the parser.
|
|
893 |
* @param element The element in which the error occured (normally 'this'
|
|
894 |
* but it could also be an expression/pattern/etc.)
|
|
895 |
* @param parser The XSLT parser to report the error to.
|
|
896 |
* @param error The error code (from util/ErrorMsg).
|
|
897 |
* @param message Any additional error message.
|
|
898 |
*/
|
|
899 |
protected void reportError(SyntaxTreeNode element, Parser parser,
|
|
900 |
String errorCode, String message) {
|
|
901 |
final ErrorMsg error = new ErrorMsg(errorCode, message, element);
|
|
902 |
parser.reportError(Constants.ERROR, error);
|
|
903 |
}
|
|
904 |
|
|
905 |
/**
|
|
906 |
* Report a recoverable error to the parser.
|
|
907 |
* @param element The element in which the error occured (normally 'this'
|
|
908 |
* but it could also be an expression/pattern/etc.)
|
|
909 |
* @param parser The XSLT parser to report the error to.
|
|
910 |
* @param error The error code (from util/ErrorMsg).
|
|
911 |
* @param message Any additional error message.
|
|
912 |
*/
|
|
913 |
protected void reportWarning(SyntaxTreeNode element, Parser parser,
|
|
914 |
String errorCode, String message) {
|
|
915 |
final ErrorMsg error = new ErrorMsg(errorCode, message, element);
|
|
916 |
parser.reportError(Constants.WARNING, error);
|
|
917 |
}
|
|
918 |
|
|
919 |
}
|