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