1 /* 2 * Copyright (c) 2015, 2017, 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 * @LastModified: Oct 2017 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 }