001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: LiteralElement.java 669372 2008-06-19 03:39:52Z zongaro $
020     */
021    
022    package org.apache.xalan.xsltc.compiler;
023    
024    import java.util.Enumeration;
025    import java.util.Hashtable;
026    import java.util.Vector;
027    
028    import org.apache.bcel.generic.ConstantPoolGen;
029    import org.apache.bcel.generic.InstructionList;
030    import org.apache.bcel.generic.PUSH;
031    import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
032    import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
033    import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
034    import org.apache.xalan.xsltc.compiler.util.Type;
035    import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
036    import org.apache.xalan.xsltc.compiler.util.Util;
037    
038    import org.apache.xml.serializer.ElemDesc;
039    import org.apache.xml.serializer.ToHTMLStream;
040    
041    /**
042     * @author Jacek Ambroziak
043     * @author Santiago Pericas-Geertsen
044     * @author Morten Jorgensen
045     */
046    final class LiteralElement extends Instruction {
047    
048        private String _name;
049        private LiteralElement _literalElemParent = null;
050        private Vector _attributeElements = null;
051        private Hashtable _accessedPrefixes = null;
052        
053        // True if all attributes of this LRE are unique, i.e. they all have
054        // different names. This flag is set to false if some attribute
055        // names are not known at compile time.
056        private boolean _allAttributesUnique = false;
057            
058        private final static String XMLNS_STRING = "xmlns";
059    
060        /**
061         * Returns the QName for this literal element
062         */
063        public QName getName() {
064            return _qname;
065        }
066     
067        /**
068         * Displays the contents of this literal element
069         */
070        public void display(int indent) {
071            indent(indent);
072            Util.println("LiteralElement name = " + _name);
073            displayContents(indent + IndentIncrement);
074        }
075    
076        /**
077         * Returns the namespace URI for which a prefix is pointing to
078         */
079        private String accessedNamespace(String prefix) {
080            if (_literalElemParent != null) {
081                String result = _literalElemParent.accessedNamespace(prefix);
082                if (result != null) {
083                    return result;
084                }
085            }       
086            return _accessedPrefixes != null ? 
087                (String) _accessedPrefixes.get(prefix) : null;
088        }
089    
090        /**
091         * Method used to keep track of what namespaces that are references by
092         * this literal element and its attributes. The output must contain a
093         * definition for each namespace, so we stuff them in a hashtable.
094         */
095        public void registerNamespace(String prefix, String uri,
096                                      SymbolTable stable, boolean declared) {
097    
098            // Check if the parent has a declaration for this namespace
099            if (_literalElemParent != null) {
100                final String parentUri = _literalElemParent.accessedNamespace(prefix);
101                if (parentUri != null && parentUri.equals(uri)) {
102                    return;
103                }
104            }
105    
106            // Check if we have any declared namesaces
107            if (_accessedPrefixes == null) {
108                _accessedPrefixes = new Hashtable();
109            }
110            else {
111                if (!declared) {
112                    // Check if this node has a declaration for this namespace
113                    final String old = (String)_accessedPrefixes.get(prefix);
114                    if (old != null) {
115                        if (old.equals(uri))
116                            return;
117                        else 
118                            prefix = stable.generateNamespacePrefix();
119                    }
120                }
121            }
122    
123            if (!prefix.equals("xml")) {
124                _accessedPrefixes.put(prefix,uri);
125            }
126        }
127    
128        /**
129         * Translates the prefix of a QName according to the rules set in
130         * the attributes of xsl:stylesheet. Also registers a QName to assure
131         * that the output element contains the necessary namespace declarations.
132         */
133        private String translateQName(QName qname, SymbolTable stable) {
134            // Break up the QName and get prefix:localname strings
135            String localname = qname.getLocalPart();
136            String prefix = qname.getPrefix();
137    
138            // Treat default namespace as "" and not null
139            if (prefix == null)
140                prefix = Constants.EMPTYSTRING;
141            else if (prefix.equals(XMLNS_STRING))
142                return(XMLNS_STRING);
143            
144            // Check if we must translate the prefix
145            final String alternative = stable.lookupPrefixAlias(prefix);
146            if (alternative != null) {
147                stable.excludeNamespaces(prefix);
148                prefix = alternative;
149            }
150    
151            // Get the namespace this prefix refers to
152            String uri = lookupNamespace(prefix);
153            if (uri == null) return(localname);
154    
155            // Register the namespace as accessed
156            registerNamespace(prefix, uri, stable, false);
157    
158            // Construct the new name for the element (may be unchanged)
159            if (prefix != Constants.EMPTYSTRING)
160                return(prefix+":"+localname);
161            else
162                return(localname);
163        }
164    
165        /**
166         * Add an attribute to this element
167         */
168        public void addAttribute(SyntaxTreeNode attribute) {
169            if (_attributeElements == null) {
170                _attributeElements = new Vector(2);
171            }
172            _attributeElements.add(attribute);
173        }
174    
175        /**
176         * Set the first attribute of this element
177         */
178        public void setFirstAttribute(SyntaxTreeNode attribute) {
179            if (_attributeElements == null) {
180                _attributeElements = new Vector(2);
181            }
182            _attributeElements.insertElementAt(attribute,0);
183        }
184    
185        /**
186         * Type-check the contents of this element. The element itself does not
187         * need any type checking as it leaves nothign on the JVM's stack.
188         */
189        public Type typeCheck(SymbolTable stable) throws TypeCheckError {
190            // Type-check all attributes
191            if (_attributeElements != null) {
192                final int count = _attributeElements.size();
193                for (int i = 0; i < count; i++) {
194                    SyntaxTreeNode node = 
195                        (SyntaxTreeNode)_attributeElements.elementAt(i);
196                    node.typeCheck(stable);
197                }
198            }
199            typeCheckContents(stable);
200            return Type.Void;
201        }
202    
203        /**
204         * This method starts at a given node, traverses all namespace mappings,
205         * and assembles a list of all prefixes that (for the given node) maps
206         * to _ANY_ namespace URI. Used by literal result elements to determine
207         */
208        public Enumeration getNamespaceScope(SyntaxTreeNode node) {
209            Hashtable all = new Hashtable();
210            
211            while (node != null) {
212                Hashtable mapping = node.getPrefixMapping();
213                if (mapping != null) {
214                    Enumeration prefixes = mapping.keys();
215                    while (prefixes.hasMoreElements()) {
216                        String prefix = (String)prefixes.nextElement();
217                        if (!all.containsKey(prefix)) {
218                            all.put(prefix, mapping.get(prefix));
219                        }
220                    }
221                }
222                node = node.getParent();
223            }
224            return(all.keys());
225        }
226    
227        /**
228         * Determines the final QName for the element and its attributes.
229         * Registers all namespaces that are used by the element/attributes
230         */
231        public void parseContents(Parser parser) {
232            final SymbolTable stable = parser.getSymbolTable();
233            stable.setCurrentNode(this);
234    
235            // Check if in a literal element context
236            SyntaxTreeNode parent = getParent();
237            if (parent != null && parent instanceof LiteralElement) {
238                _literalElemParent = (LiteralElement) parent;
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 uri = qname.getNamespace();
248                final String 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.equals(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.equals(parser.getExtensionElementPrefixes())) {
262                    stable.excludeNamespaces(val);
263                }
264                // Handle xsl:exclude-result-prefixes
265                else if (qname.equals(parser.getExcludeResultPrefixes())) {
266                    stable.excludeNamespaces(val);
267                }
268                else {
269                    // Ignore special attributes (e.g. xmlns:prefix and xmlns)
270                    final String 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 name = translateQName(qname, stable);
280                    LiteralAttribute attr = new LiteralAttribute(name, val, parser, this);
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 include = getNamespaceScope(this);
290            while (include.hasMoreElements()) {
291                final String prefix = (String)include.nextElement();
292                if (!prefix.equals("xml")) {
293                    final String 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 val = _attributes.getValue(i);
306    
307                // Handle xsl:extension-element-prefixes
308                if (qname.equals(parser.getExtensionElementPrefixes())) {
309                    stable.unExcludeNamespaces(val);
310                }
311                // Handle xsl:exclude-result-prefixes
312                else if (qname.equals(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                final SyntaxTreeNode item = (SyntaxTreeNode) elementAt(j);
348                if (item instanceof Variable) {
349                    item.translate(classGen, methodGen);
350                }
351                j++;
352            }
353    
354            // Compile code to emit namespace attributes
355            if (_accessedPrefixes != null) {
356                boolean declaresDefaultNS = false;
357                Enumeration e = _accessedPrefixes.keys();
358    
359                while (e.hasMoreElements()) {
360                    final String prefix = (String)e.nextElement();
361                    final String uri = (String)_accessedPrefixes.get(prefix);
362    
363                    if (uri != Constants.EMPTYSTRING || 
364                            prefix != Constants.EMPTYSTRING) 
365                    {
366                        if (prefix == Constants.EMPTYSTRING) {
367                            declaresDefaultNS = true;
368                        }
369                        il.append(methodGen.loadHandler());
370                        il.append(new PUSH(cpg,prefix));
371                        il.append(new PUSH(cpg,uri));
372                        il.append(methodGen.namespace());
373                    }
374                }
375    
376                /* 
377                 * If our XslElement parent redeclares the default NS, and this
378                 * element doesn't, it must be redeclared one more time.
379                 */
380                if (!declaresDefaultNS && (_parent instanceof XslElement)
381                        && ((XslElement) _parent).declaresDefaultNS()) 
382                {
383                    il.append(methodGen.loadHandler());
384                    il.append(new PUSH(cpg, Constants.EMPTYSTRING));
385                    il.append(new PUSH(cpg, Constants.EMPTYSTRING));
386                    il.append(methodGen.namespace());
387                }
388            }
389    
390            // Output all attributes
391            if (_attributeElements != null) {
392                final int count = _attributeElements.size();
393                for (int i = 0; i < count; i++) {
394                    SyntaxTreeNode node = 
395                        (SyntaxTreeNode)_attributeElements.elementAt(i);
396                    if (!(node instanceof XslAttribute)) {
397                        node.translate(classGen, methodGen);
398                    }
399                }
400            }
401            
402            // Compile code to emit attributes and child elements
403            translateContents(classGen, methodGen);
404    
405            // Compile code to emit element end tag
406            il.append(methodGen.endElement());
407        }
408    
409        /**
410         * Return true if the output method is html.
411         */
412        private boolean isHTMLOutput() {
413            return getStylesheet().getOutputMethod() == Stylesheet.HTML_OUTPUT;
414        }
415        
416        /**
417         * Return the ElemDesc object for an HTML element.
418         * Return null if the output method is not HTML or this is not a 
419         * valid HTML element.
420         */
421        public ElemDesc getElemDesc() {
422            if (isHTMLOutput()) {
423                return ToHTMLStream.getElemDesc(_name);
424            }
425            else
426                return null;
427        }
428        
429        /**
430         * Return true if all attributes of this LRE have unique names.
431         */
432        public boolean allAttributesUnique() {
433            return _allAttributesUnique;
434        }
435        
436        /**
437         * Check whether all attributes are unique.
438         */
439        private boolean checkAttributesUnique() {
440             boolean hasHiddenXslAttribute = canProduceAttributeNodes(this, true);
441             if (hasHiddenXslAttribute)
442                 return false;
443             
444             if (_attributeElements != null) {
445                 int numAttrs = _attributeElements.size();
446                 Hashtable attrsTable = null;
447                 for (int i = 0; i < numAttrs; i++) {
448                     SyntaxTreeNode node = (SyntaxTreeNode)_attributeElements.elementAt(i);
449                     
450                     if (node instanceof UseAttributeSets) {
451                         return false;
452                     }
453                     else if (node instanceof XslAttribute) {                    
454                         if (attrsTable == null) {
455                            attrsTable = new Hashtable();
456                             for (int k = 0; k < i; k++) {
457                                 SyntaxTreeNode n = (SyntaxTreeNode)_attributeElements.elementAt(k);
458                                 if (n instanceof LiteralAttribute) {
459                                     LiteralAttribute literalAttr = (LiteralAttribute)n;
460                                     attrsTable.put(literalAttr.getName(), literalAttr);
461                                 }
462                             }
463                         }
464                         
465                         XslAttribute xslAttr = (XslAttribute)node;
466                         AttributeValue attrName = xslAttr.getName();
467                         if (attrName instanceof AttributeValueTemplate) {
468                             return false;
469                         }
470                         else if (attrName instanceof SimpleAttributeValue) {
471                             SimpleAttributeValue simpleAttr = (SimpleAttributeValue)attrName;
472                             String name = simpleAttr.toString();
473                             if (name != null && attrsTable.get(name) != null)
474                                 return false;
475                             else if (name != null) {
476                                 attrsTable.put(name, xslAttr);
477                             }                       
478                         }
479                     }
480                 }
481             }
482             return true;
483        }
484        
485        /**
486         * Return true if the instructions under the given SyntaxTreeNode can produce attribute nodes
487         * to an element. Only return false when we are sure that no attribute node is produced. 
488         * Return true if we are not sure. If the flag ignoreXslAttribute is true, the direct 
489         * <xsl:attribute> children of the current node are not included in the check.
490         */
491        private boolean canProduceAttributeNodes(SyntaxTreeNode node, boolean ignoreXslAttribute) {
492            Vector contents = node.getContents();
493            int size = contents.size();
494            for (int i = 0; i < size; i++) {
495                SyntaxTreeNode child = (SyntaxTreeNode)contents.elementAt(i);
496                if (child instanceof Text) {
497                    Text text = (Text)child;
498                    if (text.isIgnore())
499                        continue;
500                    else
501                        return false;
502                }
503                // Cannot add an attribute to an element after children have been added to it.
504                // We can safely return false when the instruction can produce an output node.
505                else if (child instanceof LiteralElement
506                    || child instanceof ValueOf
507                    || child instanceof XslElement
508                    || child instanceof Comment
509                    || child instanceof Number
510                    || child instanceof ProcessingInstruction)
511                    return false;
512                else if (child instanceof XslAttribute) {
513                    if (ignoreXslAttribute)
514                        continue;
515                    else
516                        return true;
517                }
518                // In general, there is no way to check whether <xsl:call-template> or 
519                // <xsl:apply-templates> can produce attribute nodes. <xsl:copy> and
520                // <xsl:copy-of> can also copy attribute nodes to an element. Return
521                // true in those cases to be safe.
522                else if (child instanceof CallTemplate
523                    || child instanceof ApplyTemplates
524                    || child instanceof Copy
525                    || child instanceof CopyOf)
526                    return true;
527                else if ((child instanceof If
528                           || child instanceof ForEach)
529                         && canProduceAttributeNodes(child, false)) {
530                    return true;
531                }
532                else if (child instanceof Choose) {
533                    Vector chooseContents = child.getContents();
534                    int num = chooseContents.size();
535                    for (int k = 0; k < num; k++) {
536                        SyntaxTreeNode chooseChild = (SyntaxTreeNode)chooseContents.elementAt(k);
537                        if (chooseChild instanceof When || chooseChild instanceof Otherwise) {
538                            if (canProduceAttributeNodes(chooseChild, false))
539                                return true;
540                        }
541                    }
542                }
543            }
544            return false;
545        }
546        
547    }