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