- /*
- * Copyright 2001-2004 The Apache Software Foundation.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- /*
- * $Id: LiteralElement.java,v 1.25 2004/02/24 03:55:47 zongaro Exp $
- */
-
- package com.sun.org.apache.xalan.internal.xsltc.compiler;
-
- import java.util.Enumeration;
- import java.util.Hashtable;
- import java.util.Vector;
-
- import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
- import com.sun.org.apache.bcel.internal.generic.InstructionList;
- import com.sun.org.apache.bcel.internal.generic.PUSH;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
-
- import com.sun.org.apache.xml.internal.serializer.ElemDesc;
- import com.sun.org.apache.xml.internal.serializer.ToHTMLStream;
-
- /**
- * @author Jacek Ambroziak
- * @author Santiago Pericas-Geertsen
- * @author Morten Jorgensen
- */
- final class LiteralElement extends Instruction {
-
- private String _name;
- private LiteralElement _literalElemParent;
- private Vector _attributeElements = null;
- private Hashtable _accessedPrefixes = null;
-
- // True if all attributes of this LRE are unique, i.e. they all have
- // different names. This flag is set to false if some attribute
- // names are not known at compile time.
- private boolean _allAttributesUnique = false;
-
- private final static String XMLNS_STRING = "xmlns";
-
- /**
- * Returns the QName for this literal element
- */
- public QName getName() {
- return _qname;
- }
-
- /**
- * Displays the contents of this literal element
- */
- public void display(int indent) {
- indent(indent);
- Util.println("LiteralElement name = " + _name);
- displayContents(indent + IndentIncrement);
- }
-
- /**
- * Returns the namespace URI for which a prefix is pointing to
- */
- private String accessedNamespace(String prefix) {
- if (_accessedPrefixes == null)
- return(null);
- else
- return((String)_accessedPrefixes.get(prefix));
- }
-
- /**
- * Method used to keep track of what namespaces that are references by
- * this literal element and its attributes. The output must contain a
- * definition for each namespace, so we stuff them in a hashtable.
- */
- public void registerNamespace(String prefix, String uri,
- SymbolTable stable, boolean declared) {
-
- // Check if the parent has a declaration for this namespace
- if (_literalElemParent != null) {
- final String parentUri = _literalElemParent.accessedNamespace(prefix);
- if (parentUri == null) {
- _literalElemParent.registerNamespace(prefix, uri, stable, declared);
- return;
- }
- if (parentUri.equals(uri)) return;
- }
-
- // Check if we have any declared namesaces
- if (_accessedPrefixes == null) {
- _accessedPrefixes = new Hashtable();
- }
- else {
- if (!declared) {
- // Check if this node has a declaration for this namespace
- final String old = (String)_accessedPrefixes.get(prefix);
- if (old != null) {
- if (old.equals(uri))
- return;
- else
- prefix = stable.generateNamespacePrefix();
- }
- }
- }
-
- if (!prefix.equals("xml")) {
- _accessedPrefixes.put(prefix,uri);
- }
- }
-
- /**
- * Translates the prefix of a QName according to the rules set in
- * the attributes of xsl:stylesheet. Also registers a QName to assure
- * that the output element contains the necessary namespace declarations.
- */
- private String translateQName(QName qname, SymbolTable stable) {
- // Break up the QName and get prefix:localname strings
- String localname = qname.getLocalPart();
- String prefix = qname.getPrefix();
-
- // Treat default namespace as "" and not null
- if (prefix == null)
- prefix = Constants.EMPTYSTRING;
- else if (prefix.equals(XMLNS_STRING))
- return(XMLNS_STRING);
-
- // Check if we must translate the prefix
- final String alternative = stable.lookupPrefixAlias(prefix);
- if (alternative != null) {
- stable.excludeNamespaces(prefix);
- prefix = alternative;
- }
-
- // Get the namespace this prefix refers to
- String uri = lookupNamespace(prefix);
- if (uri == null) return(localname);
-
- // Register the namespace as accessed
- registerNamespace(prefix, uri, stable, false);
-
- // Construct the new name for the element (may be unchanged)
- if (prefix != Constants.EMPTYSTRING)
- return(prefix+":"+localname);
- else
- return(localname);
- }
-
- /**
- * Add an attribute to this element
- */
- public void addAttribute(SyntaxTreeNode attribute) {
- if (_attributeElements == null) {
- _attributeElements = new Vector(2);
- }
- _attributeElements.add(attribute);
- }
-
- /**
- * Set the first attribute of this element
- */
- public void setFirstAttribute(SyntaxTreeNode attribute) {
- if (_attributeElements == null) {
- _attributeElements = new Vector(2);
- }
- _attributeElements.insertElementAt(attribute,0);
- }
-
- /**
- * Type-check the contents of this element. The element itself does not
- * need any type checking as it leaves nothign on the JVM's stack.
- */
- public Type typeCheck(SymbolTable stable) throws TypeCheckError {
- // Type-check all attributes
- if (_attributeElements != null) {
- final int count = _attributeElements.size();
- for (int i = 0; i < count; i++) {
- SyntaxTreeNode node =
- (SyntaxTreeNode)_attributeElements.elementAt(i);
- node.typeCheck(stable);
- }
- }
- typeCheckContents(stable);
- return Type.Void;
- }
-
- /**
- * This method starts at a given node, traverses all namespace mappings,
- * and assembles a list of all prefixes that (for the given node) maps
- * to _ANY_ namespace URI. Used by literal result elements to determine
- */
- public Enumeration getNamespaceScope(SyntaxTreeNode node) {
- Hashtable all = new Hashtable();
-
- while (node != null) {
- Hashtable mapping = node.getPrefixMapping();
- if (mapping != null) {
- Enumeration prefixes = mapping.keys();
- while (prefixes.hasMoreElements()) {
- String prefix = (String)prefixes.nextElement();
- if (!all.containsKey(prefix)) {
- all.put(prefix, mapping.get(prefix));
- }
- }
- }
- node = node.getParent();
- }
- return(all.keys());
- }
-
- /**
- * Determines the final QName for the element and its attributes.
- * Registers all namespaces that are used by the element/attributes
- */
- public void parseContents(Parser parser) {
- final SymbolTable stable = parser.getSymbolTable();
- stable.setCurrentNode(this);
-
- // Find the closest literal element ancestor (if there is one)
- SyntaxTreeNode _literalElemParent = getParent();
- while (_literalElemParent != null && !(_literalElemParent instanceof LiteralElement)) {
- _literalElemParent = _literalElemParent.getParent();
- }
-
- if (!(_literalElemParent instanceof LiteralElement)) {
- _literalElemParent = null;
- }
-
- _name = translateQName(_qname, stable);
-
- // Process all attributes and register all namespaces they use
- final int count = _attributes.getLength();
- for (int i = 0; i < count; i++) {
- final QName qname = parser.getQName(_attributes.getQName(i));
- final String uri = qname.getNamespace();
- final String val = _attributes.getValue(i);
-
- // Handle xsl:use-attribute-sets. Attribute sets are placed first
- // in the vector or attributes to make sure that later local
- // attributes can override an attributes in the set.
- if (qname == parser.getUseAttributeSets()) {
- if (!Util.isValidQNames(val)) {
- ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, val, this);
- parser.reportError(Constants.ERROR, err);
- }
- setFirstAttribute(new UseAttributeSets(val, parser));
- }
- // Handle xsl:extension-element-prefixes
- else if (qname == parser.getExtensionElementPrefixes()) {
- stable.excludeNamespaces(val);
- }
- // Handle xsl:exclude-result-prefixes
- else if (qname == parser.getExcludeResultPrefixes()) {
- stable.excludeNamespaces(val);
- }
- else {
- // Ignore special attributes (e.g. xmlns:prefix and xmlns)
- final String prefix = qname.getPrefix();
- if (prefix != null && prefix.equals(XMLNS_PREFIX) ||
- prefix == null && qname.getLocalPart().equals("xmlns") ||
- uri != null && uri.equals(XSLT_URI))
- {
- continue;
- }
-
- // Handle all other literal attributes
- final String name = translateQName(qname, stable);
- LiteralAttribute attr = new LiteralAttribute(name, val, parser, this);
- addAttribute(attr);
- attr.setParent(this);
- attr.parseContents(parser);
- }
- }
-
- // Register all namespaces that are in scope, except for those that
- // are listed in the xsl:stylesheet element's *-prefixes attributes
- final Enumeration include = getNamespaceScope(this);
- while (include.hasMoreElements()) {
- final String prefix = (String)include.nextElement();
- if (!prefix.equals("xml")) {
- final String uri = lookupNamespace(prefix);
- if (uri != null && !stable.isExcludedNamespace(uri)) {
- registerNamespace(prefix, uri, stable, true);
- }
- }
- }
-
- parseChildren(parser);
-
- // Process all attributes and register all namespaces they use
- for (int i = 0; i < count; i++) {
- final QName qname = parser.getQName(_attributes.getQName(i));
- final String val = _attributes.getValue(i);
-
- // Handle xsl:extension-element-prefixes
- if (qname == parser.getExtensionElementPrefixes()) {
- stable.unExcludeNamespaces(val);
- }
- // Handle xsl:exclude-result-prefixes
- else if (qname == parser.getExcludeResultPrefixes()) {
- stable.unExcludeNamespaces(val);
- }
- }
- }
-
- protected boolean contextDependent() {
- return dependentContents();
- }
-
- /**
- * Compiles code that emits the literal element to the output handler,
- * first the start tag, then namespace declaration, then attributes,
- * then the element contents, and then the element end tag. Since the
- * value of an attribute may depend on a variable, variables must be
- * compiled first.
- */
- public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
-
- final ConstantPoolGen cpg = classGen.getConstantPool();
- final InstructionList il = methodGen.getInstructionList();
-
- // Check whether all attributes are unique.
- _allAttributesUnique = checkAttributesUnique();
-
- // Compile code to emit element start tag
- il.append(methodGen.loadHandler());
-
- il.append(new PUSH(cpg, _name));
- il.append(DUP2); // duplicate these 2 args for endElement
- il.append(methodGen.startElement());
-
- // The value of an attribute may depend on a (sibling) variable
- int j=0;
- while (j < elementCount())
- {
- final SyntaxTreeNode item = (SyntaxTreeNode) elementAt(j);
- if (item instanceof Variable) {
- item.translate(classGen, methodGen);
- removeElement(item); // avoid translating it twice
- /* When removing an element we do not increment j
- * but the removal will reduce the value of elementCount()
- * so this loop WILL end. The next iteration will process
- * elementAt(j), but with the old element removed
- * we are actually processing the next element.
- */
- }
- else
- j++;
- }
-
- // Compile code to emit namespace attributes
- if (_accessedPrefixes != null) {
- boolean declaresDefaultNS = false;
- Enumeration e = _accessedPrefixes.keys();
-
- while (e.hasMoreElements()) {
- final String prefix = (String)e.nextElement();
- final String uri = (String)_accessedPrefixes.get(prefix);
-
- if (uri != Constants.EMPTYSTRING ||
- prefix != Constants.EMPTYSTRING)
- {
- if (prefix == Constants.EMPTYSTRING) {
- declaresDefaultNS = true;
- }
- il.append(methodGen.loadHandler());
- il.append(new PUSH(cpg,prefix));
- il.append(new PUSH(cpg,uri));
- il.append(methodGen.namespace());
- }
- }
-
- /*
- * If our XslElement parent redeclares the default NS, and this
- * element doesn't, it must be redeclared one more time.
- */
- if (!declaresDefaultNS && (_parent instanceof XslElement)
- && ((XslElement) _parent).declaresDefaultNS())
- {
- il.append(methodGen.loadHandler());
- il.append(new PUSH(cpg, Constants.EMPTYSTRING));
- il.append(new PUSH(cpg, Constants.EMPTYSTRING));
- il.append(methodGen.namespace());
- }
- }
-
- // Output all attributes
- if (_attributeElements != null) {
- final int count = _attributeElements.size();
- for (int i = 0; i < count; i++) {
- SyntaxTreeNode node =
- (SyntaxTreeNode)_attributeElements.elementAt(i);
- if (!(node instanceof XslAttribute)) {
- node.translate(classGen, methodGen);
- }
- }
- }
-
- // Compile code to emit attributes and child elements
- translateContents(classGen, methodGen);
-
- // Compile code to emit element end tag
- il.append(methodGen.endElement());
- }
-
- /**
- * Return true if the output method is html.
- */
- private boolean isHTMLOutput() {
- return getStylesheet().getOutputMethod() == Stylesheet.HTML_OUTPUT;
- }
-
- /**
- * Return the ElemDesc object for an HTML element.
- * Return null if the output method is not HTML or this is not a
- * valid HTML element.
- */
- public ElemDesc getElemDesc() {
- if (isHTMLOutput()) {
- return ToHTMLStream.getElemDesc(_name);
- }
- else
- return null;
- }
-
- /**
- * Return true if all attributes of this LRE have unique names.
- */
- public boolean allAttributesUnique() {
- return _allAttributesUnique;
- }
-
- /**
- * Check whether all attributes are unique.
- */
- private boolean checkAttributesUnique() {
- boolean hasHiddenXslAttribute = canProduceAttributeNodes(this, true);
- if (hasHiddenXslAttribute)
- return false;
-
- if (_attributeElements != null) {
- int numAttrs = _attributeElements.size();
- Hashtable attrsTable = null;
- for (int i = 0; i < numAttrs; i++) {
- SyntaxTreeNode node = (SyntaxTreeNode)_attributeElements.elementAt(i);
-
- if (node instanceof UseAttributeSets) {
- return false;
- }
- else if (node instanceof XslAttribute) {
- if (attrsTable == null) {
- attrsTable = new Hashtable();
- for (int k = 0; k < i; k++) {
- SyntaxTreeNode n = (SyntaxTreeNode)_attributeElements.elementAt(k);
- if (n instanceof LiteralAttribute) {
- LiteralAttribute literalAttr = (LiteralAttribute)n;
- attrsTable.put(literalAttr.getName(), literalAttr);
- }
- }
- }
-
- XslAttribute xslAttr = (XslAttribute)node;
- AttributeValue attrName = xslAttr.getName();
- if (attrName instanceof AttributeValueTemplate) {
- return false;
- }
- else if (attrName instanceof SimpleAttributeValue) {
- SimpleAttributeValue simpleAttr = (SimpleAttributeValue)attrName;
- String name = simpleAttr.toString();
- if (name != null && attrsTable.get(name) != null)
- return false;
- else if (name != null) {
- attrsTable.put(name, xslAttr);
- }
- }
- }
- }
- }
- return true;
- }
-
- /**
- * Return true if the instructions under the given SyntaxTreeNode can produce attribute nodes
- * to an element. Only return false when we are sure that no attribute node is produced.
- * Return true if we are not sure. If the flag ignoreXslAttribute is true, the direct
- * <xsl:attribute> children of the current node are not included in the check.
- */
- private boolean canProduceAttributeNodes(SyntaxTreeNode node, boolean ignoreXslAttribute) {
- Vector contents = node.getContents();
- int size = contents.size();
- for (int i = 0; i < size; i++) {
- SyntaxTreeNode child = (SyntaxTreeNode)contents.elementAt(i);
- if (child instanceof Text) {
- Text text = (Text)child;
- if (text.isIgnore())
- continue;
- else
- return false;
- }
- // Cannot add an attribute to an element after children have been added to it.
- // We can safely return false when the instruction can produce an output node.
- else if (child instanceof LiteralElement
- || child instanceof ValueOf
- || child instanceof XslElement
- || child instanceof Comment
- || child instanceof Number
- || child instanceof ProcessingInstruction)
- return false;
- else if (child instanceof XslAttribute) {
- if (ignoreXslAttribute)
- continue;
- else
- return true;
- }
- // In general, there is no way to check whether <xsl:call-template> or
- // <xsl:apply-templates> can produce attribute nodes. <xsl:copy> and
- // <xsl:copy-of> can also copy attribute nodes to an element. Return
- // true in those cases to be safe.
- else if (child instanceof CallTemplate
- || child instanceof ApplyTemplates
- || child instanceof Copy
- || child instanceof CopyOf)
- return true;
- else if ((child instanceof If
- || child instanceof ForEach)
- && canProduceAttributeNodes(child, false)) {
- return true;
- }
- else if (child instanceof Choose) {
- Vector chooseContents = child.getContents();
- int num = chooseContents.size();
- for (int k = 0; k < num; k++) {
- SyntaxTreeNode chooseChild = (SyntaxTreeNode)chooseContents.elementAt(k);
- if (chooseChild instanceof When || chooseChild instanceof Otherwise) {
- if (canProduceAttributeNodes(chooseChild, false))
- return true;
- }
- }
- }
- }
- return false;
- }
-
- }