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.ALOAD;
24 import com.sun.org.apache.bcel.internal.generic.ASTORE;
25 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
26 import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
27 import com.sun.org.apache.bcel.internal.generic.InstructionList;
28 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
29 import com.sun.org.apache.bcel.internal.generic.PUSH;
30 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
31 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
32 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
33 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
34 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
35 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
36 import com.sun.org.apache.xml.internal.utils.XML11Char;
37 
38 /**
39  * @author Jacek Ambroziak
40  * @author Santiago Pericas-Geertsen
41  * @author Morten Jorgensen
42  */
43 final class XslElement extends Instruction {
44 
45     private String  _prefix;
46     private boolean _ignore = false;
47     private boolean _isLiteralName = true;
48     private AttributeValueTemplate _name;
49     private AttributeValueTemplate _namespace;
50 
51     /**
52      * Displays the contents of the element
53      */
display(int indent)54     public void display(int indent) {
55         indent(indent);
56         Util.println("Element " + _name);
57         displayContents(indent + IndentIncrement);
58     }
59 
parseContents(Parser parser)60     public void parseContents(Parser parser) {
61         final SymbolTable stable = parser.getSymbolTable();
62 
63         // Handle the 'name' attribute
64         String name = getAttribute("name");
65         if (name == EMPTYSTRING) {
66             ErrorMsg msg = new ErrorMsg(ErrorMsg.ILLEGAL_ELEM_NAME_ERR,
67                                         name, this);
68             parser.reportError(WARNING, msg);
69             parseChildren(parser);
70             _ignore = true;     // Ignore the element if the QName is invalid
71             return;
72         }
73 
74         // Get namespace attribute
75         String namespace = getAttribute("namespace");
76 
77         // Optimize compilation when name is known at compile time
78         _isLiteralName = Util.isLiteral(name);
79         if (_isLiteralName) {
80             if (!XML11Char.isXML11ValidQName(name)) {
81                 ErrorMsg msg = new ErrorMsg(ErrorMsg.ILLEGAL_ELEM_NAME_ERR,
82                                             name, this);
83                 parser.reportError(WARNING, msg);
84                 parseChildren(parser);
85                 _ignore = true;         // Ignore the element if the QName is invalid
86                 return;
87             }
88 
89             final QName qname = parser.getQNameSafe(name);
90             String prefix = qname.getPrefix();
91             String local = qname.getLocalPart();
92 
93             if (prefix == null) {
94                 prefix = EMPTYSTRING;
95             }
96 
97             if (!hasAttribute("namespace")) {
98                 namespace = lookupNamespace(prefix);
99                 if (namespace == null) {
100                     ErrorMsg err = new ErrorMsg(ErrorMsg.NAMESPACE_UNDEF_ERR,
101                                                 prefix, this);
102                     parser.reportError(WARNING, err);
103                     parseChildren(parser);
104                     _ignore = true;     // Ignore the element if prefix is undeclared
105                     return;
106                 }
107                 _prefix = prefix;
108                 _namespace = new AttributeValueTemplate(namespace, parser, this);
109             }
110             else {
111                 if (prefix == EMPTYSTRING) {
112                     if (Util.isLiteral(namespace)) {
113                         prefix = lookupPrefix(namespace);
114                         if (prefix == null) {
115                             prefix = stable.generateNamespacePrefix();
116                         }
117                     }
118 
119                     // Prepend prefix to local name
120                     final StringBuffer newName = new StringBuffer(prefix);
121                     if (prefix != EMPTYSTRING) {
122                         newName.append(':');
123                     }
124                     name = newName.append(local).toString();
125                 }
126                 _prefix = prefix;
127                 _namespace = new AttributeValueTemplate(namespace, parser, this);
128             }
129         }
130         else {
131             _namespace = (namespace == EMPTYSTRING) ? null :
132                          new AttributeValueTemplate(namespace, parser, this);
133         }
134 
135         _name = new AttributeValueTemplate(name, parser, this);
136 
137         final String useSets = getAttribute("use-attribute-sets");
138         if (useSets.length() > 0) {
139             if (!Util.isValidQNames(useSets)) {
140                 ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, useSets, this);
141                 parser.reportError(Constants.ERROR, err);
142             }
143             setFirstElement(new UseAttributeSets(useSets, parser));
144         }
145 
146         parseChildren(parser);
147     }
148 
149     /**
150      * Run type check on element name & contents
151      */
typeCheck(SymbolTable stable)152     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
153         if (!_ignore) {
154             _name.typeCheck(stable);
155             if (_namespace != null) {
156                 _namespace.typeCheck(stable);
157             }
158         }
159         typeCheckContents(stable);
160         return Type.Void;
161     }
162 
163     /**
164      * This method is called when the name of the element is known at compile time.
165      * In this case, there is no need to inspect the element name at runtime to
166      * determine if a prefix exists, needs to be generated, etc.
167      */
translateLiteral(ClassGenerator classGen, MethodGenerator methodGen)168     public void translateLiteral(ClassGenerator classGen, MethodGenerator methodGen) {
169         final ConstantPoolGen cpg = classGen.getConstantPool();
170         final InstructionList il = methodGen.getInstructionList();
171 
172         if (!_ignore) {
173             il.append(methodGen.loadHandler());
174             _name.translate(classGen, methodGen);
175             il.append(DUP2);
176             il.append(methodGen.startElement());
177 
178             if (_namespace != null) {
179                 il.append(methodGen.loadHandler());
180                 il.append(new PUSH(cpg, _prefix));
181                 _namespace.translate(classGen,methodGen);
182                 il.append(methodGen.namespace());
183             }
184         }
185 
186         translateContents(classGen, methodGen);
187 
188         if (!_ignore) {
189             il.append(methodGen.endElement());
190         }
191     }
192 
193     /**
194      * At runtime the compilation of xsl:element results in code that: (i)
195      * evaluates the avt for the name, (ii) checks for a prefix in the name
196      * (iii) generates a new prefix and create a new qname when necessary
197      * (iv) calls startElement() on the handler (v) looks up a uri in the XML
198      * when the prefix is not known at compile time (vi) calls namespace()
199      * on the handler (vii) evaluates the contents (viii) calls endElement().
200      */
translate(ClassGenerator classGen, MethodGenerator methodGen)201     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
202         final ConstantPoolGen cpg = classGen.getConstantPool();
203         final InstructionList il = methodGen.getInstructionList();
204 
205         // Optimize translation if element name is a literal
206         if (_isLiteralName) {
207             translateLiteral(classGen, methodGen);
208             return;
209         }
210 
211         if (!_ignore) {
212 
213             // if the qname is an AVT, then the qname has to be checked at runtime if it is a valid qname
214             LocalVariableGen nameValue =
215                     methodGen.addLocalVariable2("nameValue",
216                                                 Util.getJCRefType(STRING_SIG),
217                                                 null);
218 
219             // store the name into a variable first so _name.translate only needs to be called once
220             _name.translate(classGen, methodGen);
221             nameValue.setStart(il.append(new ASTORE(nameValue.getIndex())));
222             il.append(new ALOAD(nameValue.getIndex()));
223 
224             // call checkQName if the name is an AVT
225             final int check = cpg.addMethodref(BASIS_LIBRARY_CLASS, "checkQName",
226                             "("
227                             +STRING_SIG
228                             +")V");
229             il.append(new INVOKESTATIC(check));
230 
231             // Push handler for call to endElement()
232             il.append(methodGen.loadHandler());
233 
234             // load name value again
235             nameValue.setEnd(il.append(new ALOAD(nameValue.getIndex())));
236 
237             if (_namespace != null) {
238                 _namespace.translate(classGen, methodGen);
239             }
240             else {
241                 il.append(ACONST_NULL);
242             }
243 
244             // Push additional arguments
245             il.append(methodGen.loadHandler());
246             il.append(methodGen.loadDOM());
247             il.append(methodGen.loadCurrentNode());
248 
249             // Invoke BasisLibrary.startXslElemCheckQName()
250             il.append(new INVOKESTATIC(
251             cpg.addMethodref(BASIS_LIBRARY_CLASS, "startXslElement",
252                     "(" + STRING_SIG
253                     + STRING_SIG
254                     + TRANSLET_OUTPUT_SIG
255                     + DOM_INTF_SIG + "I)" + STRING_SIG)));
256 
257 
258         }
259 
260         translateContents(classGen, methodGen);
261 
262         if (!_ignore) {
263             il.append(methodGen.endElement());
264         }
265     }
266 
267     /**
268      * Override this method to make sure that xsl:attributes are not
269      * copied to output if this xsl:element is to be ignored
270      */
translateContents(ClassGenerator classGen, MethodGenerator methodGen)271     public void translateContents(ClassGenerator classGen,
272                                   MethodGenerator methodGen) {
273         final int n = elementCount();
274         for (int i = 0; i < n; i++) {
275             final SyntaxTreeNode item = getContents().get(i);
276             if (_ignore && item instanceof XslAttribute) continue;
277             item.translate(classGen, methodGen);
278         }
279     }
280 
281 }
282