1 /*
2 * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
3 * @LastModified: Oct 2017
4 */
5 /*
6 * Licensed to the Apache Software Foundation (ASF) under one or more
7 * contributor license agreements. See the NOTICE file distributed with
8 * this work for additional information regarding copyright ownership.
9 * The ASF licenses this file to You under the Apache License, Version 2.0
10 * (the "License"); you may not use this file except in compliance with
11 * the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22 package com.sun.org.apache.xalan.internal.xsltc.compiler;
23
24 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
25 import com.sun.org.apache.bcel.internal.generic.InstructionList;
26 import com.sun.org.apache.bcel.internal.generic.PUSH;
27 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
28 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
29 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
30 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
31 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
32 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
33 import com.sun.org.apache.xml.internal.serializer.ElemDesc;
34 import com.sun.org.apache.xml.internal.serializer.ToHTMLStream;
35 import java.util.ArrayList;
36 import java.util.HashMap;
37 import java.util.Hashtable;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.Set;
41
42 /**
43 * @author Jacek Ambroziak
44 * @author Santiago Pericas-Geertsen
45 * @author Morten Jorgensen
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 }
--- EOF ---