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 }