View Javadoc
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: LiteralElement.java,v 1.2.4.1 2005/09/13 12:38:33 pvedula 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.ConstantPoolGen;
31  import com.sun.org.apache.bcel.internal.generic.InstructionList;
32  import com.sun.org.apache.bcel.internal.generic.PUSH;
33  import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
34  import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
35  import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
36  import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
37  import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
38  import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
39  
40  import com.sun.org.apache.xml.internal.serializer.ElemDesc;
41  import com.sun.org.apache.xml.internal.serializer.ToHTMLStream;
42  
43  /**
44   * @author Jacek Ambroziak
45   * @author Santiago Pericas-Geertsen
46   * @author Morten Jorgensen
47   */
48  final class LiteralElement extends Instruction {
49  
50      private String _name;
51      private LiteralElement _literalElemParent = null;
52      private Vector _attributeElements = null;
53      private Hashtable _accessedPrefixes = null;
54  
55      // True if all attributes of this LRE are unique, i.e. they all have
56      // different names. This flag is set to false if some attribute
57      // names are not known at compile time.
58      private boolean _allAttributesUnique = false;
59  
60      private final static String XMLNS_STRING = "xmlns";
61  
62      /**
63       * Returns the QName for this literal element
64       */
65      public QName getName() {
66          return _qname;
67      }
68  
69      /**
70       * Displays the contents of this literal element
71       */
72      public void display(int indent) {
73          indent(indent);
74          Util.println("LiteralElement name = " + _name);
75          displayContents(indent + IndentIncrement);
76      }
77  
78      /**
79       * Returns the namespace URI for which a prefix is pointing to
80       */
81      private String accessedNamespace(String prefix) {
82          if (_literalElemParent != null) {
83              String result = _literalElemParent.accessedNamespace(prefix);
84              if (result != null) {
85                  return result;
86              }
87          }
88          return _accessedPrefixes != null ?
89              (String) _accessedPrefixes.get(prefix) : null;
90      }
91  
92      /**
93       * Method used to keep track of what namespaces that are references by
94       * this literal element and its attributes. The output must contain a
95       * definition for each namespace, so we stuff them in a hashtable.
96       */
97      public void registerNamespace(String prefix, String uri,
98                                    SymbolTable stable, boolean declared) {
99  
100         // Check if the parent has a declaration for this namespace
101         if (_literalElemParent != null) {
102             final String parentUri = _literalElemParent.accessedNamespace(prefix);
103             if (parentUri != null && parentUri.equals(uri)) {
104                 return;
105             }
106         }
107 
108         // Check if we have any declared namesaces
109         if (_accessedPrefixes == null) {
110             _accessedPrefixes = new Hashtable();
111         }
112         else {
113             if (!declared) {
114                 // Check if this node has a declaration for this namespace
115                 final String old = (String)_accessedPrefixes.get(prefix);
116                 if (old != null) {
117                     if (old.equals(uri))
118                         return;
119                     else
120                         prefix = stable.generateNamespacePrefix();
121                 }
122             }
123         }
124 
125         if (!prefix.equals("xml")) {
126             _accessedPrefixes.put(prefix,uri);
127         }
128     }
129 
130     /**
131      * Translates the prefix of a QName according to the rules set in
132      * the attributes of xsl:stylesheet. Also registers a QName to assure
133      * that the output element contains the necessary namespace declarations.
134      */
135     private String translateQName(QName qname, SymbolTable stable) {
136         // Break up the QName and get prefix:localname strings
137         String localname = qname.getLocalPart();
138         String prefix = qname.getPrefix();
139 
140         // Treat default namespace as "" and not null
141         if (prefix == null)
142             prefix = Constants.EMPTYSTRING;
143         else if (prefix.equals(XMLNS_STRING))
144             return(XMLNS_STRING);
145 
146         // Check if we must translate the prefix
147         final String alternative = stable.lookupPrefixAlias(prefix);
148         if (alternative != null) {
149             stable.excludeNamespaces(prefix);
150             prefix = alternative;
151         }
152 
153         // Get the namespace this prefix refers to
154         String uri = lookupNamespace(prefix);
155         if (uri == null) return(localname);
156 
157         // Register the namespace as accessed
158         registerNamespace(prefix, uri, stable, false);
159 
160         // Construct the new name for the element (may be unchanged)
161         if (prefix != Constants.EMPTYSTRING)
162             return(prefix+":"+localname);
163         else
164             return(localname);
165     }
166 
167     /**
168      * Add an attribute to this element
169      */
170     public void addAttribute(SyntaxTreeNode attribute) {
171         if (_attributeElements == null) {
172             _attributeElements = new Vector(2);
173         }
174         _attributeElements.add(attribute);
175     }
176 
177     /**
178      * Set the first attribute of this element
179      */
180     public void setFirstAttribute(SyntaxTreeNode attribute) {
181         if (_attributeElements == null) {
182             _attributeElements = new Vector(2);
183         }
184         _attributeElements.insertElementAt(attribute,0);
185     }
186 
187     /**
188      * Type-check the contents of this element. The element itself does not
189      * need any type checking as it leaves nothign on the JVM's stack.
190      */
191     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
192         // Type-check all attributes
193         if (_attributeElements != null) {
194             final int count = _attributeElements.size();
195             for (int i = 0; i < count; i++) {
196                 SyntaxTreeNode node =
197                     (SyntaxTreeNode)_attributeElements.elementAt(i);
198                 node.typeCheck(stable);
199             }
200         }
201         typeCheckContents(stable);
202         return Type.Void;
203     }
204 
205     /**
206      * This method starts at a given node, traverses all namespace mappings,
207      * and assembles a list of all prefixes that (for the given node) maps
208      * to _ANY_ namespace URI. Used by literal result elements to determine
209      */
210     public Enumeration getNamespaceScope(SyntaxTreeNode node) {
211         Hashtable all = new Hashtable();
212 
213         while (node != null) {
214             Hashtable mapping = node.getPrefixMapping();
215             if (mapping != null) {
216                 Enumeration prefixes = mapping.keys();
217                 while (prefixes.hasMoreElements()) {
218                     String prefix = (String)prefixes.nextElement();
219                     if (!all.containsKey(prefix)) {
220                         all.put(prefix, mapping.get(prefix));
221                     }
222                 }
223             }
224             node = node.getParent();
225         }
226         return(all.keys());
227     }
228 
229     /**
230      * Determines the final QName for the element and its attributes.
231      * Registers all namespaces that are used by the element/attributes
232      */
233     public void parseContents(Parser parser) {
234         final SymbolTable stable = parser.getSymbolTable();
235         stable.setCurrentNode(this);
236 
237         // Check if in a literal element context
238         SyntaxTreeNode parent = getParent();
239         if (parent != null && parent instanceof LiteralElement) {
240             _literalElemParent = (LiteralElement) parent;
241         }
242 
243         _name = translateQName(_qname, stable);
244 
245         // Process all attributes and register all namespaces they use
246         final int count = _attributes.getLength();
247         for (int i = 0; i < count; i++) {
248             final QName qname = parser.getQName(_attributes.getQName(i));
249             final String uri = qname.getNamespace();
250             final String val = _attributes.getValue(i);
251 
252             // Handle xsl:use-attribute-sets. Attribute sets are placed first
253             // in the vector or attributes to make sure that later local
254             // attributes can override an attributes in the set.
255             if (qname.equals(parser.getUseAttributeSets())) {
256                 if (!Util.isValidQNames(val)) {
257                     ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, val, this);
258                     parser.reportError(Constants.ERROR, err);
259                }
260                 setFirstAttribute(new UseAttributeSets(val, parser));
261             }
262             // Handle xsl:extension-element-prefixes
263             else if (qname.equals(parser.getExtensionElementPrefixes())) {
264                 stable.excludeNamespaces(val);
265             }
266             // Handle xsl:exclude-result-prefixes
267             else if (qname.equals(parser.getExcludeResultPrefixes())) {
268                 stable.excludeNamespaces(val);
269             }
270             else {
271                 // Ignore special attributes (e.g. xmlns:prefix and xmlns)
272                 final String prefix = qname.getPrefix();
273                 if (prefix != null && prefix.equals(XMLNS_PREFIX) ||
274                     prefix == null && qname.getLocalPart().equals("xmlns") ||
275                     uri != null && uri.equals(XSLT_URI))
276                 {
277                     continue;
278                 }
279 
280                 // Handle all other literal attributes
281                 final String name = translateQName(qname, stable);
282                 LiteralAttribute attr = new LiteralAttribute(name, val, parser, this);
283                 addAttribute(attr);
284                 attr.setParent(this);
285                 attr.parseContents(parser);
286             }
287         }
288 
289         // Register all namespaces that are in scope, except for those that
290         // are listed in the xsl:stylesheet element's *-prefixes attributes
291         final Enumeration include = getNamespaceScope(this);
292         while (include.hasMoreElements()) {
293             final String prefix = (String)include.nextElement();
294             if (!prefix.equals("xml")) {
295                 final String uri = lookupNamespace(prefix);
296                 if (uri != null && !stable.isExcludedNamespace(uri)) {
297                     registerNamespace(prefix, uri, stable, true);
298                 }
299             }
300         }
301 
302         parseChildren(parser);
303 
304         // Process all attributes and register all namespaces they use
305         for (int i = 0; i < count; i++) {
306             final QName qname = parser.getQName(_attributes.getQName(i));
307             final String val = _attributes.getValue(i);
308 
309             // Handle xsl:extension-element-prefixes
310             if (qname.equals(parser.getExtensionElementPrefixes())) {
311                 stable.unExcludeNamespaces(val);
312             }
313             // Handle xsl:exclude-result-prefixes
314             else if (qname.equals(parser.getExcludeResultPrefixes())) {
315                 stable.unExcludeNamespaces(val);
316             }
317         }
318     }
319 
320     protected boolean contextDependent() {
321         return dependentContents();
322     }
323 
324     /**
325      * Compiles code that emits the literal element to the output handler,
326      * first the start tag, then namespace declaration, then attributes,
327      * then the element contents, and then the element end tag. Since the
328      * value of an attribute may depend on a variable, variables must be
329      * compiled first.
330      */
331     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
332 
333         final ConstantPoolGen cpg = classGen.getConstantPool();
334         final InstructionList il = methodGen.getInstructionList();
335 
336         // Check whether all attributes are unique.
337         _allAttributesUnique = checkAttributesUnique();
338 
339         // Compile code to emit element start tag
340         il.append(methodGen.loadHandler());
341 
342         il.append(new PUSH(cpg, _name));
343         il.append(DUP2);                // duplicate these 2 args for endElement
344         il.append(methodGen.startElement());
345 
346         // The value of an attribute may depend on a (sibling) variable
347         int j=0;
348         while (j < elementCount())  {
349             final SyntaxTreeNode item = (SyntaxTreeNode) elementAt(j);
350             if (item instanceof Variable) {
351                 item.translate(classGen, methodGen);
352             }
353             j++;
354         }
355 
356         // Compile code to emit namespace attributes
357         if (_accessedPrefixes != null) {
358             boolean declaresDefaultNS = false;
359             Enumeration e = _accessedPrefixes.keys();
360 
361             while (e.hasMoreElements()) {
362                 final String prefix = (String)e.nextElement();
363                 final String uri = (String)_accessedPrefixes.get(prefix);
364 
365                 if (uri != Constants.EMPTYSTRING ||
366                         prefix != Constants.EMPTYSTRING)
367                 {
368                     if (prefix == Constants.EMPTYSTRING) {
369                         declaresDefaultNS = true;
370                     }
371                     il.append(methodGen.loadHandler());
372                     il.append(new PUSH(cpg,prefix));
373                     il.append(new PUSH(cpg,uri));
374                     il.append(methodGen.namespace());
375                 }
376             }
377 
378             /*
379              * If our XslElement parent redeclares the default NS, and this
380              * element doesn't, it must be redeclared one more time.
381              */
382             if (!declaresDefaultNS && (_parent instanceof XslElement)
383                     && ((XslElement) _parent).declaresDefaultNS())
384             {
385                 il.append(methodGen.loadHandler());
386                 il.append(new PUSH(cpg, Constants.EMPTYSTRING));
387                 il.append(new PUSH(cpg, Constants.EMPTYSTRING));
388                 il.append(methodGen.namespace());
389             }
390         }
391 
392         // Output all attributes
393         if (_attributeElements != null) {
394             final int count = _attributeElements.size();
395             for (int i = 0; i < count; i++) {
396                 SyntaxTreeNode node =
397                     (SyntaxTreeNode)_attributeElements.elementAt(i);
398                 if (!(node instanceof XslAttribute)) {
399                     node.translate(classGen, methodGen);
400                 }
401             }
402         }
403 
404         // Compile code to emit attributes and child elements
405         translateContents(classGen, methodGen);
406 
407         // Compile code to emit element end tag
408         il.append(methodGen.endElement());
409     }
410 
411     /**
412      * Return true if the output method is html.
413      */
414     private boolean isHTMLOutput() {
415         return getStylesheet().getOutputMethod() == Stylesheet.HTML_OUTPUT;
416     }
417 
418     /**
419      * Return the ElemDesc object for an HTML element.
420      * Return null if the output method is not HTML or this is not a
421      * valid HTML element.
422      */
423     public ElemDesc getElemDesc() {
424         if (isHTMLOutput()) {
425             return ToHTMLStream.getElemDesc(_name);
426         }
427         else
428             return null;
429     }
430 
431     /**
432      * Return true if all attributes of this LRE have unique names.
433      */
434     public boolean allAttributesUnique() {
435         return _allAttributesUnique;
436     }
437 
438     /**
439      * Check whether all attributes are unique.
440      */
441     private boolean checkAttributesUnique() {
442          boolean hasHiddenXslAttribute = canProduceAttributeNodes(this, true);
443          if (hasHiddenXslAttribute)
444              return false;
445 
446          if (_attributeElements != null) {
447              int numAttrs = _attributeElements.size();
448              Hashtable attrsTable = null;
449              for (int i = 0; i < numAttrs; i++) {
450                  SyntaxTreeNode node = (SyntaxTreeNode)_attributeElements.elementAt(i);
451 
452                  if (node instanceof UseAttributeSets) {
453                      return false;
454                  }
455                  else if (node instanceof XslAttribute) {
456                      if (attrsTable == null) {
457                         attrsTable = new Hashtable();
458                          for (int k = 0; k < i; k++) {
459                              SyntaxTreeNode n = (SyntaxTreeNode)_attributeElements.elementAt(k);
460                              if (n instanceof LiteralAttribute) {
461                                  LiteralAttribute literalAttr = (LiteralAttribute)n;
462                                  attrsTable.put(literalAttr.getName(), literalAttr);
463                              }
464                          }
465                      }
466 
467                      XslAttribute xslAttr = (XslAttribute)node;
468                      AttributeValue attrName = xslAttr.getName();
469                      if (attrName instanceof AttributeValueTemplate) {
470                          return false;
471                      }
472                      else if (attrName instanceof SimpleAttributeValue) {
473                          SimpleAttributeValue simpleAttr = (SimpleAttributeValue)attrName;
474                          String name = simpleAttr.toString();
475                          if (name != null && attrsTable.get(name) != null)
476                              return false;
477                          else if (name != null) {
478                              attrsTable.put(name, xslAttr);
479                          }
480                      }
481                  }
482              }
483          }
484          return true;
485     }
486 
487     /**
488      * Return true if the instructions under the given SyntaxTreeNode can produce attribute nodes
489      * to an element. Only return false when we are sure that no attribute node is produced.
490      * Return true if we are not sure. If the flag ignoreXslAttribute is true, the direct
491      * <xsl:attribute> children of the current node are not included in the check.
492      */
493     private boolean canProduceAttributeNodes(SyntaxTreeNode node, boolean ignoreXslAttribute) {
494         Vector contents = node.getContents();
495         int size = contents.size();
496         for (int i = 0; i < size; i++) {
497             SyntaxTreeNode child = (SyntaxTreeNode)contents.elementAt(i);
498             if (child instanceof Text) {
499                 Text text = (Text)child;
500                 if (text.isIgnore())
501                     continue;
502                 else
503                     return false;
504             }
505             // Cannot add an attribute to an element after children have been added to it.
506             // We can safely return false when the instruction can produce an output node.
507             else if (child instanceof LiteralElement
508                 || child instanceof ValueOf
509                 || child instanceof XslElement
510                 || child instanceof Comment
511                 || child instanceof Number
512                 || child instanceof ProcessingInstruction)
513                 return false;
514             else if (child instanceof XslAttribute) {
515                 if (ignoreXslAttribute)
516                     continue;
517                 else
518                     return true;
519             }
520             // In general, there is no way to check whether <xsl:call-template> or
521             // <xsl:apply-templates> can produce attribute nodes. <xsl:copy> and
522             // <xsl:copy-of> can also copy attribute nodes to an element. Return
523             // true in those cases to be safe.
524             else if (child instanceof CallTemplate
525                 || child instanceof ApplyTemplates
526                 || child instanceof Copy
527                 || child instanceof CopyOf)
528                 return true;
529             else if ((child instanceof If
530                        || child instanceof ForEach)
531                      && canProduceAttributeNodes(child, false)) {
532                 return true;
533             }
534             else if (child instanceof Choose) {
535                 Vector chooseContents = child.getContents();
536                 int num = chooseContents.size();
537                 for (int k = 0; k < num; k++) {
538                     SyntaxTreeNode chooseChild = (SyntaxTreeNode)chooseContents.elementAt(k);
539                     if (chooseChild instanceof When || chooseChild instanceof Otherwise) {
540                         if (canProduceAttributeNodes(chooseChild, false))
541                             return true;
542                     }
543                 }
544             }
545         }
546         return false;
547     }
548 
549 }