1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Licensed to the Apache Software Foundation (ASF) under one or more
   6  * contributor license agreements.  See the NOTICE file distributed with
   7  * this work for additional information regarding copyright ownership.
   8  * The ASF licenses this file to You under the Apache License, Version 2.0
   9  * (the "License"); you may not use this file except in compliance with
  10  * the License.  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 com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  27 import com.sun.org.apache.bcel.internal.generic.InstructionList;
  28 import com.sun.org.apache.bcel.internal.generic.PUSH;
  29 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  30 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
  31 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  32 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
  33 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
  34 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
  35 import com.sun.org.apache.xml.internal.serializer.ElemDesc;
  36 import com.sun.org.apache.xml.internal.serializer.ToHTMLStream;
  37 import java.util.ArrayList;
  38 import java.util.HashMap;
  39 import java.util.Hashtable;
  40 import java.util.List;
  41 import java.util.Map;
  42 import java.util.Set;
  43 
  44 /**
  45  * @author Jacek Ambroziak
  46  * @author Santiago Pericas-Geertsen
  47  * @author Morten Jorgensen
  48  */
  49 final class LiteralElement extends Instruction {
  50 
  51     private String _name;
  52     private LiteralElement _literalElemParent = null;
  53     private List<SyntaxTreeNode> _attributeElements = null;
  54     private Map<String, String> _accessedPrefixes = null;
  55 
  56     // True if all attributes of this LRE are unique, i.e. they all have
  57     // different names. This flag is set to false if some attribute
  58     // names are not known at compile time.
  59     private boolean _allAttributesUnique = false;
  60 
  61     private final static String XMLNS_STRING = "xmlns";
  62 
  63     /**
  64      * Returns the QName for this literal element
  65      */
  66     public QName getName() {
  67         return _qname;
  68     }
  69 
  70     /**
  71      * Displays the contents of this literal element
  72      */
  73     public void display(int indent) {
  74         indent(indent);
  75         Util.println("LiteralElement name = " + _name);
  76         displayContents(indent + IndentIncrement);
  77     }
  78 
  79     /**
  80      * Returns the namespace URI for which a prefix is pointing to
  81      */
  82     private String accessedNamespace(String prefix) {
  83         if (_literalElemParent != null) {
  84             String result = _literalElemParent.accessedNamespace(prefix);
  85             if (result != null) {
  86                 return result;
  87             }
  88         }
  89         return _accessedPrefixes != null ? _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 map.
  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 namespaces
 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 = _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 ArrayList<>(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 ArrayList<>(2);
 183         }
 184         _attributeElements.add(0, attribute);
 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             for (SyntaxTreeNode node : _attributeElements) {
 195                 node.typeCheck(stable);
 196             }
 197         }
 198         typeCheckContents(stable);
 199         return Type.Void;
 200     }
 201 
 202     /**
 203      * This method starts at a given node, traverses all namespace mappings,
 204      * and assembles a list of all prefixes that (for the given node) maps
 205      * to _ANY_ namespace URI. Used by literal result elements to determine
 206      */
 207     public Set<Map.Entry<String, String>> getNamespaceScope(SyntaxTreeNode node) {
 208         Map<String, String> all = new HashMap<>();
 209 
 210         while (node != null) {
 211             Map<String, String> mapping = node.getPrefixMapping();
 212             if (mapping != null) {
 213                 mapping.entrySet().stream().forEach((entry) -> {
 214                     all.putIfAbsent(entry.getKey(), entry.getValue());
 215                 });
 216             }
 217             node = node.getParent();
 218         }
 219         return all.entrySet();
 220     }
 221 
 222     /**
 223      * Determines the final QName for the element and its attributes.
 224      * Registers all namespaces that are used by the element/attributes
 225      */
 226     public void parseContents(Parser parser) {
 227         final SymbolTable stable = parser.getSymbolTable();
 228         stable.setCurrentNode(this);
 229 
 230         // Check if in a literal element context
 231         SyntaxTreeNode parent = getParent();
 232         if (parent != null && parent instanceof LiteralElement) {
 233             _literalElemParent = (LiteralElement) parent;
 234         }
 235 
 236         _name = translateQName(_qname, stable);
 237 
 238         // Process all attributes and register all namespaces they use
 239         final int count = _attributes.getLength();
 240         for (int i = 0; i < count; i++) {
 241             final QName qname = parser.getQName(_attributes.getQName(i));
 242             final String uri = qname.getNamespace();
 243             final String val = _attributes.getValue(i);
 244 
 245             // Handle xsl:use-attribute-sets. Attribute sets are placed first
 246             // in the vector or attributes to make sure that later local
 247             // attributes can override an attributes in the set.
 248             if (qname.equals(parser.getUseAttributeSets())) {
 249                 if (!Util.isValidQNames(val)) {
 250                     ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, val, this);
 251                     parser.reportError(Constants.ERROR, err);
 252                }
 253                 setFirstAttribute(new UseAttributeSets(val, parser));
 254             }
 255             // Handle xsl:extension-element-prefixes
 256             else if (qname.equals(parser.getExtensionElementPrefixes())) {
 257                 stable.excludeNamespaces(val);
 258             }
 259             // Handle xsl:exclude-result-prefixes
 260             else if (qname.equals(parser.getExcludeResultPrefixes())) {
 261                 stable.excludeNamespaces(val);
 262             }
 263             else {
 264                 // Ignore special attributes (e.g. xmlns:prefix and xmlns)
 265                 final String prefix = qname.getPrefix();
 266                 if (prefix != null && prefix.equals(XMLNS_PREFIX) ||
 267                     prefix == null && qname.getLocalPart().equals("xmlns") ||
 268                     uri != null && uri.equals(XSLT_URI))
 269                 {
 270                     continue;
 271                 }
 272 
 273                 // Handle all other literal attributes
 274                 final String name = translateQName(qname, stable);
 275                 LiteralAttribute attr = new LiteralAttribute(name, val, parser, this);
 276                 addAttribute(attr);
 277                 attr.setParent(this);
 278                 attr.parseContents(parser);
 279             }
 280         }
 281 
 282         // Register all namespaces that are in scope, except for those that
 283         // are listed in the xsl:stylesheet element's *-prefixes attributes
 284         Set<Map.Entry<String, String>> include = getNamespaceScope(this);
 285         for (Map.Entry<String, String> entry : include) {
 286             final String prefix = entry.getKey();
 287             if (!prefix.equals("xml")) {
 288                 final String uri = lookupNamespace(prefix);
 289                 if (uri != null && !stable.isExcludedNamespace(uri)) {
 290                     registerNamespace(prefix, uri, stable, true);
 291                 }
 292             }
 293         }
 294 
 295         parseChildren(parser);
 296 
 297         // Process all attributes and register all namespaces they use
 298         for (int i = 0; i < count; i++) {
 299             final QName qname = parser.getQName(_attributes.getQName(i));
 300             final String val = _attributes.getValue(i);
 301 
 302             // Handle xsl:extension-element-prefixes
 303             if (qname.equals(parser.getExtensionElementPrefixes())) {
 304                 stable.unExcludeNamespaces(val);
 305             }
 306             // Handle xsl:exclude-result-prefixes
 307             else if (qname.equals(parser.getExcludeResultPrefixes())) {
 308                 stable.unExcludeNamespaces(val);
 309             }
 310         }
 311     }
 312 
 313     protected boolean contextDependent() {
 314         return dependentContents();
 315     }
 316 
 317     /**
 318      * Compiles code that emits the literal element to the output handler,
 319      * first the start tag, then namespace declaration, then attributes,
 320      * then the element contents, and then the element end tag. Since the
 321      * value of an attribute may depend on a variable, variables must be
 322      * compiled first.
 323      */
 324     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
 325 
 326         final ConstantPoolGen cpg = classGen.getConstantPool();
 327         final InstructionList il = methodGen.getInstructionList();
 328 
 329         // Check whether all attributes are unique.
 330         _allAttributesUnique = checkAttributesUnique();
 331 
 332         // Compile code to emit element start tag
 333         il.append(methodGen.loadHandler());
 334 
 335         il.append(new PUSH(cpg, _name));
 336         il.append(DUP2);                // duplicate these 2 args for endElement
 337         il.append(methodGen.startElement());
 338 
 339         // The value of an attribute may depend on a (sibling) variable
 340         int j=0;
 341         while (j < elementCount())  {
 342             final SyntaxTreeNode item = (SyntaxTreeNode) elementAt(j);
 343             if (item instanceof Variable) {
 344                 item.translate(classGen, methodGen);
 345             }
 346             j++;
 347         }
 348 
 349         // Compile code to emit namespace attributes
 350         if (_accessedPrefixes != null) {
 351             boolean declaresDefaultNS = false;
 352 
 353             for (Map.Entry<String, String> entry : _accessedPrefixes.entrySet()) {
 354                 final String prefix = entry.getKey();
 355                 final String uri = entry.getValue();
 356 
 357                 if (uri != Constants.EMPTYSTRING ||
 358                         prefix != Constants.EMPTYSTRING)
 359                 {
 360                     if (prefix == Constants.EMPTYSTRING) {
 361                         declaresDefaultNS = true;
 362                     }
 363                     il.append(methodGen.loadHandler());
 364                     il.append(new PUSH(cpg,prefix));
 365                     il.append(new PUSH(cpg,uri));
 366                     il.append(methodGen.namespace());
 367                 }
 368             }
 369 
 370             /*
 371              * If our XslElement parent redeclares the default NS, and this
 372              * element doesn't, it must be redeclared one more time.
 373              */
 374             if (!declaresDefaultNS && (_parent instanceof XslElement)
 375                     && ((XslElement) _parent).declaresDefaultNS())
 376             {
 377                 il.append(methodGen.loadHandler());
 378                 il.append(new PUSH(cpg, Constants.EMPTYSTRING));
 379                 il.append(new PUSH(cpg, Constants.EMPTYSTRING));
 380                 il.append(methodGen.namespace());
 381             }
 382         }
 383 
 384         // Output all attributes
 385         if (_attributeElements != null) {
 386             for (SyntaxTreeNode node : _attributeElements) {
 387                 if (!(node instanceof XslAttribute)) {
 388                     node.translate(classGen, methodGen);
 389                 }
 390             }
 391         }
 392 
 393         // Compile code to emit attributes and child elements
 394         translateContents(classGen, methodGen);
 395 
 396         // Compile code to emit element end tag
 397         il.append(methodGen.endElement());
 398     }
 399 
 400     /**
 401      * Return true if the output method is html.
 402      */
 403     private boolean isHTMLOutput() {
 404         return getStylesheet().getOutputMethod() == Stylesheet.HTML_OUTPUT;
 405     }
 406 
 407     /**
 408      * Return the ElemDesc object for an HTML element.
 409      * Return null if the output method is not HTML or this is not a
 410      * valid HTML element.
 411      */
 412     public ElemDesc getElemDesc() {
 413         if (isHTMLOutput()) {
 414             return ToHTMLStream.getElemDesc(_name);
 415         }
 416         else
 417             return null;
 418     }
 419 
 420     /**
 421      * Return true if all attributes of this LRE have unique names.
 422      */
 423     public boolean allAttributesUnique() {
 424         return _allAttributesUnique;
 425     }
 426 
 427     /**
 428      * Check whether all attributes are unique.
 429      */
 430     private boolean checkAttributesUnique() {
 431          boolean hasHiddenXslAttribute = canProduceAttributeNodes(this, true);
 432          if (hasHiddenXslAttribute)
 433              return false;
 434 
 435          if (_attributeElements != null) {
 436              int numAttrs = _attributeElements.size();
 437              Map<String, SyntaxTreeNode> attrsTable = null;
 438              for (int i = 0; i < numAttrs; i++) {
 439                  SyntaxTreeNode node = _attributeElements.get(i);
 440 
 441                  if (node instanceof UseAttributeSets) {
 442                      return false;
 443                  }
 444                  else if (node instanceof XslAttribute) {
 445                      if (attrsTable == null) {
 446                         attrsTable = new HashMap<>();
 447                          for (int k = 0; k < i; k++) {
 448                              SyntaxTreeNode n = _attributeElements.get(k);
 449                              if (n instanceof LiteralAttribute) {
 450                                  LiteralAttribute literalAttr = (LiteralAttribute)n;
 451                                  attrsTable.put(literalAttr.getName(), literalAttr);
 452                              }
 453                          }
 454                      }
 455 
 456                      XslAttribute xslAttr = (XslAttribute)node;
 457                      AttributeValue attrName = xslAttr.getName();
 458                      if (attrName instanceof AttributeValueTemplate) {
 459                          return false;
 460                      }
 461                      else if (attrName instanceof SimpleAttributeValue) {
 462                          SimpleAttributeValue simpleAttr = (SimpleAttributeValue)attrName;
 463                          String name = simpleAttr.toString();
 464                          if (name != null && attrsTable.get(name) != null)
 465                              return false;
 466                          else if (name != null) {
 467                              attrsTable.put(name, xslAttr);
 468                          }
 469                      }
 470                  }
 471              }
 472          }
 473          return true;
 474     }
 475 
 476     /**
 477      * Return true if the instructions under the given SyntaxTreeNode can produce attribute nodes
 478      * to an element. Only return false when we are sure that no attribute node is produced.
 479      * Return true if we are not sure. If the flag ignoreXslAttribute is true, the direct
 480      * <xsl:attribute> children of the current node are not included in the check.
 481      */
 482     private boolean canProduceAttributeNodes(SyntaxTreeNode node, boolean ignoreXslAttribute) {
 483         List<SyntaxTreeNode> contents = node.getContents();
 484         for (SyntaxTreeNode child : contents) {
 485             if (child instanceof Text) {
 486                 Text text = (Text)child;
 487                 if (text.isIgnore())
 488                     continue;
 489                 else
 490                     return false;
 491             }
 492             // Cannot add an attribute to an element after children have been added to it.
 493             // We can safely return false when the instruction can produce an output node.
 494             else if (child instanceof LiteralElement
 495                 || child instanceof ValueOf
 496                 || child instanceof XslElement
 497                 || child instanceof Comment
 498                 || child instanceof Number
 499                 || child instanceof ProcessingInstruction)
 500                 return false;
 501             else if (child instanceof XslAttribute) {
 502                 if (ignoreXslAttribute)
 503                     continue;
 504                 else
 505                     return true;
 506             }
 507             // In general, there is no way to check whether <xsl:call-template> or
 508             // <xsl:apply-templates> can produce attribute nodes. <xsl:copy> and
 509             // <xsl:copy-of> can also copy attribute nodes to an element. Return
 510             // true in those cases to be safe.
 511             else if (child instanceof CallTemplate
 512                 || child instanceof ApplyTemplates
 513                 || child instanceof Copy
 514                 || child instanceof CopyOf)
 515                 return true;
 516             else if ((child instanceof If
 517                        || child instanceof ForEach)
 518                      && canProduceAttributeNodes(child, false)) {
 519                 return true;
 520             }
 521             else if (child instanceof Choose) {
 522                 List<SyntaxTreeNode> chooseContents = child.getContents();
 523                 for (SyntaxTreeNode chooseChild : chooseContents) {
 524                     if (chooseChild instanceof When || chooseChild instanceof Otherwise) {
 525                         if (canProduceAttributeNodes(chooseChild, false))
 526                             return true;
 527                     }
 528                 }
 529             }
 530         }
 531         return false;
 532     }
 533 
 534 }