001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 /* 019 * $Id: LiteralElement.java 669372 2008-06-19 03:39:52Z zongaro $ 020 */ 021 022 package org.apache.xalan.xsltc.compiler; 023 024 import java.util.Enumeration; 025 import java.util.Hashtable; 026 import java.util.Vector; 027 028 import org.apache.bcel.generic.ConstantPoolGen; 029 import org.apache.bcel.generic.InstructionList; 030 import org.apache.bcel.generic.PUSH; 031 import org.apache.xalan.xsltc.compiler.util.ClassGenerator; 032 import org.apache.xalan.xsltc.compiler.util.ErrorMsg; 033 import org.apache.xalan.xsltc.compiler.util.MethodGenerator; 034 import org.apache.xalan.xsltc.compiler.util.Type; 035 import org.apache.xalan.xsltc.compiler.util.TypeCheckError; 036 import org.apache.xalan.xsltc.compiler.util.Util; 037 038 import org.apache.xml.serializer.ElemDesc; 039 import org.apache.xml.serializer.ToHTMLStream; 040 041 /** 042 * @author Jacek Ambroziak 043 * @author Santiago Pericas-Geertsen 044 * @author Morten Jorgensen 045 */ 046 final class LiteralElement extends Instruction { 047 048 private String _name; 049 private LiteralElement _literalElemParent = null; 050 private Vector _attributeElements = null; 051 private Hashtable _accessedPrefixes = null; 052 053 // True if all attributes of this LRE are unique, i.e. they all have 054 // different names. This flag is set to false if some attribute 055 // names are not known at compile time. 056 private boolean _allAttributesUnique = false; 057 058 private final static String XMLNS_STRING = "xmlns"; 059 060 /** 061 * Returns the QName for this literal element 062 */ 063 public QName getName() { 064 return _qname; 065 } 066 067 /** 068 * Displays the contents of this literal element 069 */ 070 public void display(int indent) { 071 indent(indent); 072 Util.println("LiteralElement name = " + _name); 073 displayContents(indent + IndentIncrement); 074 } 075 076 /** 077 * Returns the namespace URI for which a prefix is pointing to 078 */ 079 private String accessedNamespace(String prefix) { 080 if (_literalElemParent != null) { 081 String result = _literalElemParent.accessedNamespace(prefix); 082 if (result != null) { 083 return result; 084 } 085 } 086 return _accessedPrefixes != null ? 087 (String) _accessedPrefixes.get(prefix) : null; 088 } 089 090 /** 091 * Method used to keep track of what namespaces that are references by 092 * this literal element and its attributes. The output must contain a 093 * definition for each namespace, so we stuff them in a hashtable. 094 */ 095 public void registerNamespace(String prefix, String uri, 096 SymbolTable stable, boolean declared) { 097 098 // Check if the parent has a declaration for this namespace 099 if (_literalElemParent != null) { 100 final String parentUri = _literalElemParent.accessedNamespace(prefix); 101 if (parentUri != null && parentUri.equals(uri)) { 102 return; 103 } 104 } 105 106 // Check if we have any declared namesaces 107 if (_accessedPrefixes == null) { 108 _accessedPrefixes = new Hashtable(); 109 } 110 else { 111 if (!declared) { 112 // Check if this node has a declaration for this namespace 113 final String old = (String)_accessedPrefixes.get(prefix); 114 if (old != null) { 115 if (old.equals(uri)) 116 return; 117 else 118 prefix = stable.generateNamespacePrefix(); 119 } 120 } 121 } 122 123 if (!prefix.equals("xml")) { 124 _accessedPrefixes.put(prefix,uri); 125 } 126 } 127 128 /** 129 * Translates the prefix of a QName according to the rules set in 130 * the attributes of xsl:stylesheet. Also registers a QName to assure 131 * that the output element contains the necessary namespace declarations. 132 */ 133 private String translateQName(QName qname, SymbolTable stable) { 134 // Break up the QName and get prefix:localname strings 135 String localname = qname.getLocalPart(); 136 String prefix = qname.getPrefix(); 137 138 // Treat default namespace as "" and not null 139 if (prefix == null) 140 prefix = Constants.EMPTYSTRING; 141 else if (prefix.equals(XMLNS_STRING)) 142 return(XMLNS_STRING); 143 144 // Check if we must translate the prefix 145 final String alternative = stable.lookupPrefixAlias(prefix); 146 if (alternative != null) { 147 stable.excludeNamespaces(prefix); 148 prefix = alternative; 149 } 150 151 // Get the namespace this prefix refers to 152 String uri = lookupNamespace(prefix); 153 if (uri == null) return(localname); 154 155 // Register the namespace as accessed 156 registerNamespace(prefix, uri, stable, false); 157 158 // Construct the new name for the element (may be unchanged) 159 if (prefix != Constants.EMPTYSTRING) 160 return(prefix+":"+localname); 161 else 162 return(localname); 163 } 164 165 /** 166 * Add an attribute to this element 167 */ 168 public void addAttribute(SyntaxTreeNode attribute) { 169 if (_attributeElements == null) { 170 _attributeElements = new Vector(2); 171 } 172 _attributeElements.add(attribute); 173 } 174 175 /** 176 * Set the first attribute of this element 177 */ 178 public void setFirstAttribute(SyntaxTreeNode attribute) { 179 if (_attributeElements == null) { 180 _attributeElements = new Vector(2); 181 } 182 _attributeElements.insertElementAt(attribute,0); 183 } 184 185 /** 186 * Type-check the contents of this element. The element itself does not 187 * need any type checking as it leaves nothign on the JVM's stack. 188 */ 189 public Type typeCheck(SymbolTable stable) throws TypeCheckError { 190 // Type-check all attributes 191 if (_attributeElements != null) { 192 final int count = _attributeElements.size(); 193 for (int i = 0; i < count; i++) { 194 SyntaxTreeNode node = 195 (SyntaxTreeNode)_attributeElements.elementAt(i); 196 node.typeCheck(stable); 197 } 198 } 199 typeCheckContents(stable); 200 return Type.Void; 201 } 202 203 /** 204 * This method starts at a given node, traverses all namespace mappings, 205 * and assembles a list of all prefixes that (for the given node) maps 206 * to _ANY_ namespace URI. Used by literal result elements to determine 207 */ 208 public Enumeration getNamespaceScope(SyntaxTreeNode node) { 209 Hashtable all = new Hashtable(); 210 211 while (node != null) { 212 Hashtable mapping = node.getPrefixMapping(); 213 if (mapping != null) { 214 Enumeration prefixes = mapping.keys(); 215 while (prefixes.hasMoreElements()) { 216 String prefix = (String)prefixes.nextElement(); 217 if (!all.containsKey(prefix)) { 218 all.put(prefix, mapping.get(prefix)); 219 } 220 } 221 } 222 node = node.getParent(); 223 } 224 return(all.keys()); 225 } 226 227 /** 228 * Determines the final QName for the element and its attributes. 229 * Registers all namespaces that are used by the element/attributes 230 */ 231 public void parseContents(Parser parser) { 232 final SymbolTable stable = parser.getSymbolTable(); 233 stable.setCurrentNode(this); 234 235 // Check if in a literal element context 236 SyntaxTreeNode parent = getParent(); 237 if (parent != null && parent instanceof LiteralElement) { 238 _literalElemParent = (LiteralElement) parent; 239 } 240 241 _name = translateQName(_qname, stable); 242 243 // Process all attributes and register all namespaces they use 244 final int count = _attributes.getLength(); 245 for (int i = 0; i < count; i++) { 246 final QName qname = parser.getQName(_attributes.getQName(i)); 247 final String uri = qname.getNamespace(); 248 final String val = _attributes.getValue(i); 249 250 // Handle xsl:use-attribute-sets. Attribute sets are placed first 251 // in the vector or attributes to make sure that later local 252 // attributes can override an attributes in the set. 253 if (qname.equals(parser.getUseAttributeSets())) { 254 if (!Util.isValidQNames(val)) { 255 ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, val, this); 256 parser.reportError(Constants.ERROR, err); 257 } 258 setFirstAttribute(new UseAttributeSets(val, parser)); 259 } 260 // Handle xsl:extension-element-prefixes 261 else if (qname.equals(parser.getExtensionElementPrefixes())) { 262 stable.excludeNamespaces(val); 263 } 264 // Handle xsl:exclude-result-prefixes 265 else if (qname.equals(parser.getExcludeResultPrefixes())) { 266 stable.excludeNamespaces(val); 267 } 268 else { 269 // Ignore special attributes (e.g. xmlns:prefix and xmlns) 270 final String prefix = qname.getPrefix(); 271 if (prefix != null && prefix.equals(XMLNS_PREFIX) || 272 prefix == null && qname.getLocalPart().equals("xmlns") || 273 uri != null && uri.equals(XSLT_URI)) 274 { 275 continue; 276 } 277 278 // Handle all other literal attributes 279 final String name = translateQName(qname, stable); 280 LiteralAttribute attr = new LiteralAttribute(name, val, parser, this); 281 addAttribute(attr); 282 attr.setParent(this); 283 attr.parseContents(parser); 284 } 285 } 286 287 // Register all namespaces that are in scope, except for those that 288 // are listed in the xsl:stylesheet element's *-prefixes attributes 289 final Enumeration include = getNamespaceScope(this); 290 while (include.hasMoreElements()) { 291 final String prefix = (String)include.nextElement(); 292 if (!prefix.equals("xml")) { 293 final String uri = lookupNamespace(prefix); 294 if (uri != null && !stable.isExcludedNamespace(uri)) { 295 registerNamespace(prefix, uri, stable, true); 296 } 297 } 298 } 299 300 parseChildren(parser); 301 302 // Process all attributes and register all namespaces they use 303 for (int i = 0; i < count; i++) { 304 final QName qname = parser.getQName(_attributes.getQName(i)); 305 final String val = _attributes.getValue(i); 306 307 // Handle xsl:extension-element-prefixes 308 if (qname.equals(parser.getExtensionElementPrefixes())) { 309 stable.unExcludeNamespaces(val); 310 } 311 // Handle xsl:exclude-result-prefixes 312 else if (qname.equals(parser.getExcludeResultPrefixes())) { 313 stable.unExcludeNamespaces(val); 314 } 315 } 316 } 317 318 protected boolean contextDependent() { 319 return dependentContents(); 320 } 321 322 /** 323 * Compiles code that emits the literal element to the output handler, 324 * first the start tag, then namespace declaration, then attributes, 325 * then the element contents, and then the element end tag. Since the 326 * value of an attribute may depend on a variable, variables must be 327 * compiled first. 328 */ 329 public void translate(ClassGenerator classGen, MethodGenerator methodGen) { 330 331 final ConstantPoolGen cpg = classGen.getConstantPool(); 332 final InstructionList il = methodGen.getInstructionList(); 333 334 // Check whether all attributes are unique. 335 _allAttributesUnique = checkAttributesUnique(); 336 337 // Compile code to emit element start tag 338 il.append(methodGen.loadHandler()); 339 340 il.append(new PUSH(cpg, _name)); 341 il.append(DUP2); // duplicate these 2 args for endElement 342 il.append(methodGen.startElement()); 343 344 // The value of an attribute may depend on a (sibling) variable 345 int j=0; 346 while (j < elementCount()) { 347 final SyntaxTreeNode item = (SyntaxTreeNode) elementAt(j); 348 if (item instanceof Variable) { 349 item.translate(classGen, methodGen); 350 } 351 j++; 352 } 353 354 // Compile code to emit namespace attributes 355 if (_accessedPrefixes != null) { 356 boolean declaresDefaultNS = false; 357 Enumeration e = _accessedPrefixes.keys(); 358 359 while (e.hasMoreElements()) { 360 final String prefix = (String)e.nextElement(); 361 final String uri = (String)_accessedPrefixes.get(prefix); 362 363 if (uri != Constants.EMPTYSTRING || 364 prefix != Constants.EMPTYSTRING) 365 { 366 if (prefix == Constants.EMPTYSTRING) { 367 declaresDefaultNS = true; 368 } 369 il.append(methodGen.loadHandler()); 370 il.append(new PUSH(cpg,prefix)); 371 il.append(new PUSH(cpg,uri)); 372 il.append(methodGen.namespace()); 373 } 374 } 375 376 /* 377 * If our XslElement parent redeclares the default NS, and this 378 * element doesn't, it must be redeclared one more time. 379 */ 380 if (!declaresDefaultNS && (_parent instanceof XslElement) 381 && ((XslElement) _parent).declaresDefaultNS()) 382 { 383 il.append(methodGen.loadHandler()); 384 il.append(new PUSH(cpg, Constants.EMPTYSTRING)); 385 il.append(new PUSH(cpg, Constants.EMPTYSTRING)); 386 il.append(methodGen.namespace()); 387 } 388 } 389 390 // Output all attributes 391 if (_attributeElements != null) { 392 final int count = _attributeElements.size(); 393 for (int i = 0; i < count; i++) { 394 SyntaxTreeNode node = 395 (SyntaxTreeNode)_attributeElements.elementAt(i); 396 if (!(node instanceof XslAttribute)) { 397 node.translate(classGen, methodGen); 398 } 399 } 400 } 401 402 // Compile code to emit attributes and child elements 403 translateContents(classGen, methodGen); 404 405 // Compile code to emit element end tag 406 il.append(methodGen.endElement()); 407 } 408 409 /** 410 * Return true if the output method is html. 411 */ 412 private boolean isHTMLOutput() { 413 return getStylesheet().getOutputMethod() == Stylesheet.HTML_OUTPUT; 414 } 415 416 /** 417 * Return the ElemDesc object for an HTML element. 418 * Return null if the output method is not HTML or this is not a 419 * valid HTML element. 420 */ 421 public ElemDesc getElemDesc() { 422 if (isHTMLOutput()) { 423 return ToHTMLStream.getElemDesc(_name); 424 } 425 else 426 return null; 427 } 428 429 /** 430 * Return true if all attributes of this LRE have unique names. 431 */ 432 public boolean allAttributesUnique() { 433 return _allAttributesUnique; 434 } 435 436 /** 437 * Check whether all attributes are unique. 438 */ 439 private boolean checkAttributesUnique() { 440 boolean hasHiddenXslAttribute = canProduceAttributeNodes(this, true); 441 if (hasHiddenXslAttribute) 442 return false; 443 444 if (_attributeElements != null) { 445 int numAttrs = _attributeElements.size(); 446 Hashtable attrsTable = null; 447 for (int i = 0; i < numAttrs; i++) { 448 SyntaxTreeNode node = (SyntaxTreeNode)_attributeElements.elementAt(i); 449 450 if (node instanceof UseAttributeSets) { 451 return false; 452 } 453 else if (node instanceof XslAttribute) { 454 if (attrsTable == null) { 455 attrsTable = new Hashtable(); 456 for (int k = 0; k < i; k++) { 457 SyntaxTreeNode n = (SyntaxTreeNode)_attributeElements.elementAt(k); 458 if (n instanceof LiteralAttribute) { 459 LiteralAttribute literalAttr = (LiteralAttribute)n; 460 attrsTable.put(literalAttr.getName(), literalAttr); 461 } 462 } 463 } 464 465 XslAttribute xslAttr = (XslAttribute)node; 466 AttributeValue attrName = xslAttr.getName(); 467 if (attrName instanceof AttributeValueTemplate) { 468 return false; 469 } 470 else if (attrName instanceof SimpleAttributeValue) { 471 SimpleAttributeValue simpleAttr = (SimpleAttributeValue)attrName; 472 String name = simpleAttr.toString(); 473 if (name != null && attrsTable.get(name) != null) 474 return false; 475 else if (name != null) { 476 attrsTable.put(name, xslAttr); 477 } 478 } 479 } 480 } 481 } 482 return true; 483 } 484 485 /** 486 * Return true if the instructions under the given SyntaxTreeNode can produce attribute nodes 487 * to an element. Only return false when we are sure that no attribute node is produced. 488 * Return true if we are not sure. If the flag ignoreXslAttribute is true, the direct 489 * <xsl:attribute> children of the current node are not included in the check. 490 */ 491 private boolean canProduceAttributeNodes(SyntaxTreeNode node, boolean ignoreXslAttribute) { 492 Vector contents = node.getContents(); 493 int size = contents.size(); 494 for (int i = 0; i < size; i++) { 495 SyntaxTreeNode child = (SyntaxTreeNode)contents.elementAt(i); 496 if (child instanceof Text) { 497 Text text = (Text)child; 498 if (text.isIgnore()) 499 continue; 500 else 501 return false; 502 } 503 // Cannot add an attribute to an element after children have been added to it. 504 // We can safely return false when the instruction can produce an output node. 505 else if (child instanceof LiteralElement 506 || child instanceof ValueOf 507 || child instanceof XslElement 508 || child instanceof Comment 509 || child instanceof Number 510 || child instanceof ProcessingInstruction) 511 return false; 512 else if (child instanceof XslAttribute) { 513 if (ignoreXslAttribute) 514 continue; 515 else 516 return true; 517 } 518 // In general, there is no way to check whether <xsl:call-template> or 519 // <xsl:apply-templates> can produce attribute nodes. <xsl:copy> and 520 // <xsl:copy-of> can also copy attribute nodes to an element. Return 521 // true in those cases to be safe. 522 else if (child instanceof CallTemplate 523 || child instanceof ApplyTemplates 524 || child instanceof Copy 525 || child instanceof CopyOf) 526 return true; 527 else if ((child instanceof If 528 || child instanceof ForEach) 529 && canProduceAttributeNodes(child, false)) { 530 return true; 531 } 532 else if (child instanceof Choose) { 533 Vector chooseContents = child.getContents(); 534 int num = chooseContents.size(); 535 for (int k = 0; k < num; k++) { 536 SyntaxTreeNode chooseChild = (SyntaxTreeNode)chooseContents.elementAt(k); 537 if (chooseChild instanceof When || chooseChild instanceof Otherwise) { 538 if (canProduceAttributeNodes(chooseChild, false)) 539 return true; 540 } 541 } 542 } 543 } 544 return false; 545 } 546 547 }