KickJava   Java API By Example, From Geeks To Geeks.

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


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: LiteralElement.java,v 1.25 2004/02/24 03:55:47 zongaro Exp $
18  */

19
20 package org.apache.xalan.xsltc.compiler;
21
22 import java.util.Enumeration JavaDoc;
23 import java.util.Hashtable JavaDoc;
24 import java.util.Vector JavaDoc;
25
26 import org.apache.bcel.generic.ConstantPoolGen;
27 import org.apache.bcel.generic.InstructionList;
28 import org.apache.bcel.generic.PUSH;
29 import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
30 import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
31 import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
32 import org.apache.xalan.xsltc.compiler.util.Type;
33 import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
34 import org.apache.xalan.xsltc.compiler.util.Util;
35
36 import org.apache.xml.serializer.ElemDesc;
37 import org.apache.xml.serializer.ToHTMLStream;
38
39 /**
40  * @author Jacek Ambroziak
41  * @author Santiago Pericas-Geertsen
42  * @author Morten Jorgensen
43  */

44 final class LiteralElement extends Instruction {
45
46     private String JavaDoc _name;
47     private LiteralElement _literalElemParent;
48     private Vector JavaDoc _attributeElements = null;
49     private Hashtable JavaDoc _accessedPrefixes = null;
50     
51     // True if all attributes of this LRE are unique, i.e. they all have
52
// different names. This flag is set to false if some attribute
53
// names are not known at compile time.
54
private boolean _allAttributesUnique = false;
55         
56     private final static String JavaDoc XMLNS_STRING = "xmlns";
57
58     /**
59      * Returns the QName for this literal element
60      */

61     public QName getName() {
62     return _qname;
63     }
64  
65     /**
66      * Displays the contents of this literal element
67      */

68     public void display(int indent) {
69     indent(indent);
70     Util.println("LiteralElement name = " + _name);
71     displayContents(indent + IndentIncrement);
72     }
73
74     /**
75      * Returns the namespace URI for which a prefix is pointing to
76      */

77     private String JavaDoc accessedNamespace(String JavaDoc prefix) {
78     if (_accessedPrefixes == null)
79         return(null);
80     else
81         return((String JavaDoc)_accessedPrefixes.get(prefix));
82     }
83
84     /**
85      * Method used to keep track of what namespaces that are references by
86      * this literal element and its attributes. The output must contain a
87      * definition for each namespace, so we stuff them in a hashtable.
88      */

89     public void registerNamespace(String JavaDoc prefix, String JavaDoc uri,
90                   SymbolTable stable, boolean declared) {
91
92     // Check if the parent has a declaration for this namespace
93
if (_literalElemParent != null) {
94         final String JavaDoc parentUri = _literalElemParent.accessedNamespace(prefix);
95         if (parentUri == null) {
96         _literalElemParent.registerNamespace(prefix, uri, stable, declared);
97         return;
98         }
99         if (parentUri.equals(uri)) return;
100     }
101
102     // Check if we have any declared namesaces
103
if (_accessedPrefixes == null) {
104         _accessedPrefixes = new Hashtable JavaDoc();
105     }
106     else {
107         if (!declared) {
108         // Check if this node has a declaration for this namespace
109
final String JavaDoc old = (String JavaDoc)_accessedPrefixes.get(prefix);
110         if (old != null) {
111             if (old.equals(uri))
112             return;
113             else
114             prefix = stable.generateNamespacePrefix();
115         }
116         }
117     }
118
119     if (!prefix.equals("xml")) {
120         _accessedPrefixes.put(prefix,uri);
121     }
122     }
123
124     /**
125      * Translates the prefix of a QName according to the rules set in
126      * the attributes of xsl:stylesheet. Also registers a QName to assure
127      * that the output element contains the necessary namespace declarations.
128      */

129     private String JavaDoc translateQName(QName qname, SymbolTable stable) {
130     // Break up the QName and get prefix:localname strings
131
String JavaDoc localname = qname.getLocalPart();
132     String JavaDoc prefix = qname.getPrefix();
133
134     // Treat default namespace as "" and not null
135
if (prefix == null)
136         prefix = Constants.EMPTYSTRING;
137     else if (prefix.equals(XMLNS_STRING))
138         return(XMLNS_STRING);
139     
140     // Check if we must translate the prefix
141
final String JavaDoc alternative = stable.lookupPrefixAlias(prefix);
142     if (alternative != null) {
143         stable.excludeNamespaces(prefix);
144         prefix = alternative;
145     }
146
147     // Get the namespace this prefix refers to
148
String JavaDoc uri = lookupNamespace(prefix);
149     if (uri == null) return(localname);
150
151     // Register the namespace as accessed
152
registerNamespace(prefix, uri, stable, false);
153
154     // Construct the new name for the element (may be unchanged)
155
if (prefix != Constants.EMPTYSTRING)
156         return(prefix+":"+localname);
157     else
158         return(localname);
159     }
160
161     /**
162      * Add an attribute to this element
163      */

164     public void addAttribute(SyntaxTreeNode attribute) {
165     if (_attributeElements == null) {
166         _attributeElements = new Vector JavaDoc(2);
167     }
168     _attributeElements.add(attribute);
169     }
170
171     /**
172      * Set the first attribute of this element
173      */

174     public void setFirstAttribute(SyntaxTreeNode attribute) {
175     if (_attributeElements == null) {
176         _attributeElements = new Vector JavaDoc(2);
177     }
178     _attributeElements.insertElementAt(attribute,0);
179     }
180
181     /**
182      * Type-check the contents of this element. The element itself does not
183      * need any type checking as it leaves nothign on the JVM's stack.
184      */

185     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
186     // Type-check all attributes
187
if (_attributeElements != null) {
188         final int count = _attributeElements.size();
189         for (int i = 0; i < count; i++) {
190         SyntaxTreeNode node =
191             (SyntaxTreeNode)_attributeElements.elementAt(i);
192         node.typeCheck(stable);
193         }
194     }
195     typeCheckContents(stable);
196     return Type.Void;
197     }
198
199     /**
200      * This method starts at a given node, traverses all namespace mappings,
201      * and assembles a list of all prefixes that (for the given node) maps
202      * to _ANY_ namespace URI. Used by literal result elements to determine
203      */

204     public Enumeration JavaDoc getNamespaceScope(SyntaxTreeNode node) {
205     Hashtable JavaDoc all = new Hashtable JavaDoc();
206     
207     while (node != null) {
208         Hashtable JavaDoc mapping = node.getPrefixMapping();
209         if (mapping != null) {
210         Enumeration JavaDoc prefixes = mapping.keys();
211         while (prefixes.hasMoreElements()) {
212             String JavaDoc prefix = (String JavaDoc)prefixes.nextElement();
213             if (!all.containsKey(prefix)) {
214             all.put(prefix, mapping.get(prefix));
215             }
216         }
217         }
218         node = node.getParent();
219     }
220     return(all.keys());
221     }
222
223     /**
224      * Determines the final QName for the element and its attributes.
225      * Registers all namespaces that are used by the element/attributes
226      */

227     public void parseContents(Parser parser) {
228     final SymbolTable stable = parser.getSymbolTable();
229     stable.setCurrentNode(this);
230
231     // Find the closest literal element ancestor (if there is one)
232
SyntaxTreeNode _literalElemParent = getParent();
233     while (_literalElemParent != null && !(_literalElemParent instanceof LiteralElement)) {
234         _literalElemParent = _literalElemParent.getParent();
235     }
236
237     if (!(_literalElemParent instanceof LiteralElement)) {
238         _literalElemParent = null;
239     }
240
241     _name = translateQName(_qname, stable);
242
243     // Process all attributes and register all namespaces they use
244
final int count = _attributes.getLength();
245     for (int i = 0; i < count; i++) {
246         final QName qname = parser.getQName(_attributes.getQName(i));
247         final String JavaDoc uri = qname.getNamespace();
248         final String JavaDoc val = _attributes.getValue(i);
249
250         // Handle xsl:use-attribute-sets. Attribute sets are placed first
251
// in the vector or attributes to make sure that later local
252
// attributes can override an attributes in the set.
253
if (qname == parser.getUseAttributeSets()) {
254                 if (!Util.isValidQNames(val)) {
255                     ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, val, this);
256                     parser.reportError(Constants.ERROR, err);
257                }
258         setFirstAttribute(new UseAttributeSets(val, parser));
259         }
260         // Handle xsl:extension-element-prefixes
261
else if (qname == parser.getExtensionElementPrefixes()) {
262         stable.excludeNamespaces(val);
263         }
264         // Handle xsl:exclude-result-prefixes
265
else if (qname == parser.getExcludeResultPrefixes()) {
266         stable.excludeNamespaces(val);
267         }
268         else {
269         // Ignore special attributes (e.g. xmlns:prefix and xmlns)
270
final String JavaDoc prefix = qname.getPrefix();
271         if (prefix != null && prefix.equals(XMLNS_PREFIX) ||
272             prefix == null && qname.getLocalPart().equals("xmlns") ||
273             uri != null && uri.equals(XSLT_URI))
274         {
275             continue;
276         }
277
278         // Handle all other literal attributes
279
final String JavaDoc name = translateQName(qname, stable);
280         LiteralAttribute attr = new LiteralAttribute(name, val, parser);
281         addAttribute(attr);
282         attr.setParent(this);
283         attr.parseContents(parser);
284         }
285     }
286
287     // Register all namespaces that are in scope, except for those that
288
// are listed in the xsl:stylesheet element's *-prefixes attributes
289
final Enumeration JavaDoc include = getNamespaceScope(this);
290     while (include.hasMoreElements()) {
291         final String JavaDoc prefix = (String JavaDoc)include.nextElement();
292         if (!prefix.equals("xml")) {
293         final String JavaDoc uri = lookupNamespace(prefix);
294         if (uri != null && !stable.isExcludedNamespace(uri)) {
295             registerNamespace(prefix, uri, stable, true);
296         }
297         }
298     }
299
300     parseChildren(parser);
301
302     // Process all attributes and register all namespaces they use
303
for (int i = 0; i < count; i++) {
304         final QName qname = parser.getQName(_attributes.getQName(i));
305         final String JavaDoc val = _attributes.getValue(i);
306
307         // Handle xsl:extension-element-prefixes
308
if (qname == parser.getExtensionElementPrefixes()) {
309         stable.unExcludeNamespaces(val);
310         }
311         // Handle xsl:exclude-result-prefixes
312
else if (qname == parser.getExcludeResultPrefixes()) {
313         stable.unExcludeNamespaces(val);
314         }
315     }
316     }
317
318     protected boolean contextDependent() {
319     return dependentContents();
320     }
321
322     /**
323      * Compiles code that emits the literal element to the output handler,
324      * first the start tag, then namespace declaration, then attributes,
325      * then the element contents, and then the element end tag. Since the
326      * value of an attribute may depend on a variable, variables must be
327      * compiled first.
328      */

329     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
330
331     final ConstantPoolGen cpg = classGen.getConstantPool();
332     final InstructionList il = methodGen.getInstructionList();
333
334         // Check whether all attributes are unique.
335
_allAttributesUnique = checkAttributesUnique();
336
337     // Compile code to emit element start tag
338
il.append(methodGen.loadHandler());
339     
340     il.append(new PUSH(cpg, _name));
341     il.append(DUP2); // duplicate these 2 args for endElement
342
il.append(methodGen.startElement());
343
344     // The value of an attribute may depend on a (sibling) variable
345
int j=0;
346     while (j < elementCount())
347     {
348         final SyntaxTreeNode item = (SyntaxTreeNode) elementAt(j);
349         if (item instanceof Variable) {
350             item.translate(classGen, methodGen);
351             removeElement(item); // avoid translating it twice
352
/* When removing an element we do not increment j
353              * but the removal will reduce the value of elementCount()
354              * so this loop WILL end. The next iteration will process
355              * elementAt(j), but with the old element removed
356              * we are actually processing the next element.
357              */

358         }
359         else
360             j++;
361     }
362
363     // Compile code to emit namespace attributes
364
if (_accessedPrefixes != null) {
365         boolean declaresDefaultNS = false;
366         Enumeration JavaDoc e = _accessedPrefixes.keys();
367
368         while (e.hasMoreElements()) {
369         final String JavaDoc prefix = (String JavaDoc)e.nextElement();
370         final String JavaDoc uri = (String JavaDoc)_accessedPrefixes.get(prefix);
371
372         if (uri != Constants.EMPTYSTRING ||
373             prefix != Constants.EMPTYSTRING)
374         {
375             if (prefix == Constants.EMPTYSTRING) {
376             declaresDefaultNS = true;
377             }
378             il.append(methodGen.loadHandler());
379             il.append(new PUSH(cpg,prefix));
380             il.append(new PUSH(cpg,uri));
381             il.append(methodGen.namespace());
382         }
383         }
384
385         /*
386          * If our XslElement parent redeclares the default NS, and this
387          * element doesn't, it must be redeclared one more time.
388          */

389         if (!declaresDefaultNS && (_parent instanceof XslElement)
390             && ((XslElement) _parent).declaresDefaultNS())
391         {
392         il.append(methodGen.loadHandler());
393         il.append(new PUSH(cpg, Constants.EMPTYSTRING));
394         il.append(new PUSH(cpg, Constants.EMPTYSTRING));
395         il.append(methodGen.namespace());
396         }
397     }
398
399     // Output all attributes
400
if (_attributeElements != null) {
401         final int count = _attributeElements.size();
402         for (int i = 0; i < count; i++) {
403         SyntaxTreeNode node =
404             (SyntaxTreeNode)_attributeElements.elementAt(i);
405         if (!(node instanceof XslAttribute)) {
406             node.translate(classGen, methodGen);
407             }
408         }
409     }
410     
411     // Compile code to emit attributes and child elements
412
translateContents(classGen, methodGen);
413
414     // Compile code to emit element end tag
415
il.append(methodGen.endElement());
416     }
417
418     /**
419      * Return true if the output method is html.
420      */

421     private boolean isHTMLOutput() {
422         return getStylesheet().getOutputMethod() == Stylesheet.HTML_OUTPUT;
423     }
424     
425     /**
426      * Return the ElemDesc object for an HTML element.
427      * Return null if the output method is not HTML or this is not a
428      * valid HTML element.
429      */

430     public ElemDesc getElemDesc() {
431         if (isHTMLOutput()) {
432             return ToHTMLStream.getElemDesc(_name);
433         }
434         else
435             return null;
436     }
437     
438     /**
439      * Return true if all attributes of this LRE have unique names.
440      */

441     public boolean allAttributesUnique() {
442         return _allAttributesUnique;
443     }
444     
445     /**
446      * Check whether all attributes are unique.
447      */

448     private boolean checkAttributesUnique() {
449          boolean hasHiddenXslAttribute = canProduceAttributeNodes(this, true);
450          if (hasHiddenXslAttribute)
451              return false;
452          
453          if (_attributeElements != null) {
454              int numAttrs = _attributeElements.size();
455              Hashtable JavaDoc attrsTable = null;
456              for (int i = 0; i < numAttrs; i++) {
457                  SyntaxTreeNode node = (SyntaxTreeNode)_attributeElements.elementAt(i);
458                  
459                  if (node instanceof UseAttributeSets) {
460                      return false;
461                  }
462                  else if (node instanceof XslAttribute) {
463                      if (attrsTable == null) {
464                         attrsTable = new Hashtable JavaDoc();
465                          for (int k = 0; k < i; k++) {
466                              SyntaxTreeNode n = (SyntaxTreeNode)_attributeElements.elementAt(k);
467                              if (n instanceof LiteralAttribute) {
468                                  LiteralAttribute literalAttr = (LiteralAttribute)n;
469                                  attrsTable.put(literalAttr.getName(), literalAttr);
470                              }
471                          }
472                      }
473                      
474                      XslAttribute xslAttr = (XslAttribute)node;
475                      AttributeValue attrName = xslAttr.getName();
476                      if (attrName instanceof AttributeValueTemplate) {
477                          return false;
478                      }
479                      else if (attrName instanceof SimpleAttributeValue) {
480                          SimpleAttributeValue simpleAttr = (SimpleAttributeValue)attrName;
481                          String JavaDoc name = simpleAttr.toString();
482                          if (name != null && attrsTable.get(name) != null)
483                              return false;
484                          else if (name != null) {
485                              attrsTable.put(name, xslAttr);
486                          }
487                      }
488                  }
489              }
490          }
491          return true;
492     }
493     
494     /**
495      * Return true if the instructions under the given SyntaxTreeNode can produce attribute nodes
496      * to an element. Only return false when we are sure that no attribute node is produced.
497      * Return true if we are not sure. If the flag ignoreXslAttribute is true, the direct
498      * <xsl:attribute> children of the current node are not included in the check.
499      */

500     private boolean canProduceAttributeNodes(SyntaxTreeNode node, boolean ignoreXslAttribute) {
501         Vector JavaDoc contents = node.getContents();
502         int size = contents.size();
503         for (int i = 0; i < size; i++) {
504             SyntaxTreeNode child = (SyntaxTreeNode)contents.elementAt(i);
505             if (child instanceof Text) {
506                 Text text = (Text)child;
507                 if (text.isIgnore())
508                     continue;
509                 else
510                     return false;
511             }
512             // Cannot add an attribute to an element after children have been added to it.
513
// We can safely return false when the instruction can produce an output node.
514
else if (child instanceof LiteralElement
515             || child instanceof ValueOf
516             || child instanceof XslElement
517             || child instanceof Comment
518             || child instanceof Number JavaDoc
519             || child instanceof ProcessingInstruction)
520                 return false;
521             else if (child instanceof XslAttribute) {
522                 if (ignoreXslAttribute)
523                     continue;
524                 else
525                     return true;
526             }
527             // In general, there is no way to check whether <xsl:call-template> or
528
// <xsl:apply-templates> can produce attribute nodes. <xsl:copy> and
529
// <xsl:copy-of> can also copy attribute nodes to an element. Return
530
// true in those cases to be safe.
531
else if (child instanceof CallTemplate
532                 || child instanceof ApplyTemplates
533                 || child instanceof Copy
534                 || child instanceof CopyOf)
535                 return true;
536             else if ((child instanceof If
537                        || child instanceof ForEach)
538                      && canProduceAttributeNodes(child, false)) {
539                 return true;
540             }
541             else if (child instanceof Choose) {
542                 Vector JavaDoc chooseContents = child.getContents();
543                 int num = chooseContents.size();
544                 for (int k = 0; k < num; k++) {
545                     SyntaxTreeNode chooseChild = (SyntaxTreeNode)chooseContents.elementAt(k);
546                     if (chooseChild instanceof When || chooseChild instanceof Otherwise) {
547                         if (canProduceAttributeNodes(chooseChild, false))
548                             return true;
549                     }
550                 }
551             }
552         }
553         return false;
554     }
555     
556 }
557
Popular Tags