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