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 }