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 }