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: XslElement.java 1225842 2011-12-30 15:14:35Z mrglavas $
020 */
021
022 package org.apache.xalan.xsltc.compiler;
023
024 import org.apache.bcel.generic.ALOAD;
025 import org.apache.bcel.generic.ASTORE;
026 import org.apache.bcel.generic.ConstantPoolGen;
027 import org.apache.bcel.generic.GETSTATIC;
028 import org.apache.bcel.generic.INVOKESTATIC;
029 import org.apache.bcel.generic.InstructionList;
030 import org.apache.bcel.generic.LocalVariableGen;
031 import org.apache.bcel.generic.PUSH;
032 import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
033 import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
034 import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
035 import org.apache.xalan.xsltc.compiler.util.Type;
036 import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
037 import org.apache.xalan.xsltc.compiler.util.Util;
038 import org.apache.xml.utils.XML11Char;
039
040 /**
041 * @author Jacek Ambroziak
042 * @author Santiago Pericas-Geertsen
043 * @author Morten Jorgensen
044 */
045 final class XslElement extends Instruction {
046
047 private String _prefix;
048 private boolean _ignore = false;
049 private boolean _isLiteralName = true;
050 private AttributeValueTemplate _name;
051 private AttributeValueTemplate _namespace;
052
053 /**
054 * Displays the contents of the element
055 */
056 public void display(int indent) {
057 indent(indent);
058 Util.println("Element " + _name);
059 displayContents(indent + IndentIncrement);
060 }
061
062 /**
063 * This method is now deprecated. The new implemation of this class
064 * never declares the default NS.
065 */
066 public boolean declaresDefaultNS() {
067 return false;
068 }
069
070 public void parseContents(Parser parser) {
071 final SymbolTable stable = parser.getSymbolTable();
072
073 // Handle the 'name' attribute
074 String name = getAttribute("name");
075 if (name == EMPTYSTRING) {
076 ErrorMsg msg = new ErrorMsg(ErrorMsg.ILLEGAL_ELEM_NAME_ERR,
077 name, this);
078 parser.reportError(WARNING, msg);
079 parseChildren(parser);
080 _ignore = true; // Ignore the element if the QName is invalid
081 return;
082 }
083
084 // Get namespace attribute
085 String namespace = getAttribute("namespace");
086
087 // Optimize compilation when name is known at compile time
088 _isLiteralName = Util.isLiteral(name);
089 if (_isLiteralName) {
090 if (!XML11Char.isXML11ValidQName(name)) {
091 ErrorMsg msg = new ErrorMsg(ErrorMsg.ILLEGAL_ELEM_NAME_ERR,
092 name, this);
093 parser.reportError(WARNING, msg);
094 parseChildren(parser);
095 _ignore = true; // Ignore the element if the QName is invalid
096 return;
097 }
098
099 final QName qname = parser.getQNameSafe(name);
100 String prefix = qname.getPrefix();
101 String local = qname.getLocalPart();
102
103 if (prefix == null) {
104 prefix = EMPTYSTRING;
105 }
106
107 if (!hasAttribute("namespace")) {
108 namespace = lookupNamespace(prefix);
109 if (namespace == null) {
110 ErrorMsg err = new ErrorMsg(ErrorMsg.NAMESPACE_UNDEF_ERR,
111 prefix, this);
112 parser.reportError(WARNING, err);
113 parseChildren(parser);
114 _ignore = true; // Ignore the element if prefix is undeclared
115 return;
116 }
117 _prefix = prefix;
118 _namespace = new AttributeValueTemplate(namespace, parser, this);
119 }
120 else {
121 if (prefix == EMPTYSTRING) {
122 if (Util.isLiteral(namespace)) {
123 prefix = lookupPrefix(namespace);
124 if (prefix == null) {
125 prefix = stable.generateNamespacePrefix();
126 }
127 }
128
129 // Prepend prefix to local name
130 final StringBuffer newName = new StringBuffer(prefix);
131 if (prefix != EMPTYSTRING) {
132 newName.append(':');
133 }
134 name = newName.append(local).toString();
135 }
136 _prefix = prefix;
137 _namespace = new AttributeValueTemplate(namespace, parser, this);
138 }
139 }
140 else {
141 // name attribute contains variable parts. If there is no namespace
142 // attribute, the generated code needs to be prepared to look up
143 // any prefix in the stylesheet at run-time.
144 _namespace = (namespace == EMPTYSTRING) ? null :
145 new AttributeValueTemplate(namespace, parser, this);
146 }
147
148 _name = new AttributeValueTemplate(name, parser, this);
149
150 final String useSets = getAttribute("use-attribute-sets");
151 if (useSets.length() > 0) {
152 if (!Util.isValidQNames(useSets)) {
153 ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, useSets, this);
154 parser.reportError(Constants.ERROR, err);
155 }
156 setFirstElement(new UseAttributeSets(useSets, parser));
157 }
158
159 parseChildren(parser);
160 }
161
162 /**
163 * Run type check on element name & contents
164 */
165 public Type typeCheck(SymbolTable stable) throws TypeCheckError {
166 if (!_ignore) {
167 _name.typeCheck(stable);
168 if (_namespace != null) {
169 _namespace.typeCheck(stable);
170 }
171 }
172 typeCheckContents(stable);
173 return Type.Void;
174 }
175
176 /**
177 * This method is called when the name of the element is known at compile time.
178 * In this case, there is no need to inspect the element name at runtime to
179 * determine if a prefix exists, needs to be generated, etc.
180 */
181 public void translateLiteral(ClassGenerator classGen, MethodGenerator methodGen) {
182 final ConstantPoolGen cpg = classGen.getConstantPool();
183 final InstructionList il = methodGen.getInstructionList();
184
185 if (!_ignore) {
186 il.append(methodGen.loadHandler());
187 _name.translate(classGen, methodGen);
188 il.append(DUP2);
189 il.append(methodGen.startElement());
190
191 if (_namespace != null) {
192 il.append(methodGen.loadHandler());
193 il.append(new PUSH(cpg, _prefix));
194 _namespace.translate(classGen,methodGen);
195 il.append(methodGen.namespace());
196 }
197 }
198
199 translateContents(classGen, methodGen);
200
201 if (!_ignore) {
202 il.append(methodGen.endElement());
203 }
204 }
205
206 /**
207 * At runtime the compilation of xsl:element results in code that: (i)
208 * evaluates the avt for the name, (ii) checks for a prefix in the name
209 * (iii) generates a new prefix and create a new qname when necessary
210 * (iv) calls startElement() on the handler (v) looks up a uri in the XML
211 * when the prefix is not known at compile time (vi) calls namespace()
212 * on the handler (vii) evaluates the contents (viii) calls endElement().
213 */
214 public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
215 LocalVariableGen local = null;
216 final ConstantPoolGen cpg = classGen.getConstantPool();
217 final InstructionList il = methodGen.getInstructionList();
218
219 // Optimize translation if element name is a literal
220 if (_isLiteralName) {
221 translateLiteral(classGen, methodGen);
222 return;
223 }
224
225 if (!_ignore) {
226
227 // if the qname is an AVT, then the qname has to be checked at runtime if it is a valid qname
228 LocalVariableGen nameValue =
229 methodGen.addLocalVariable2("nameValue",
230 Util.getJCRefType(STRING_SIG),
231 null);
232
233 // store the name into a variable first so _name.translate only needs to be called once
234 _name.translate(classGen, methodGen);
235 nameValue.setStart(il.append(new ASTORE(nameValue.getIndex())));
236 il.append(new ALOAD(nameValue.getIndex()));
237
238 // call checkQName if the name is an AVT
239 final int check = cpg.addMethodref(BASIS_LIBRARY_CLASS, "checkQName",
240 "("
241 +STRING_SIG
242 +")V");
243 il.append(new INVOKESTATIC(check));
244
245 // Push handler for call to endElement()
246 il.append(methodGen.loadHandler());
247
248 // load name value again
249 nameValue.setEnd(il.append(new ALOAD(nameValue.getIndex())));
250
251 if (_namespace != null) {
252 _namespace.translate(classGen, methodGen);
253 }
254 else {
255 // If name is an AVT and namespace is not specified, need to
256 // look up any prefix in the stylesheet by calling
257 // BasisLibrary.lookupStylesheetQNameNamespace(
258 // name, stylesheetNode, ancestorsArray,
259 // prefixURIsIndexArray, prefixURIPairsArray,
260 // !ignoreDefaultNamespace)
261 String transletClassName = getXSLTC().getClassName();
262 il.append(DUP);
263 il.append(new PUSH(cpg, getNodeIDForStylesheetNSLookup()));
264 il.append(new GETSTATIC(cpg.addFieldref(
265 transletClassName,
266 STATIC_NS_ANCESTORS_ARRAY_FIELD,
267 NS_ANCESTORS_INDEX_SIG)));
268 il.append(new GETSTATIC(cpg.addFieldref(
269 transletClassName,
270 STATIC_PREFIX_URIS_IDX_ARRAY_FIELD,
271 PREFIX_URIS_IDX_SIG)));
272 il.append(new GETSTATIC(cpg.addFieldref(
273 transletClassName,
274 STATIC_PREFIX_URIS_ARRAY_FIELD,
275 PREFIX_URIS_ARRAY_SIG)));
276 // Default namespace is significant
277 il.append(ICONST_0);
278 il.append(
279 new INVOKESTATIC(
280 cpg.addMethodref(BASIS_LIBRARY_CLASS,
281 LOOKUP_STYLESHEET_QNAME_NS_REF,
282 LOOKUP_STYLESHEET_QNAME_NS_SIG)));
283 }
284
285 // Push additional arguments
286 il.append(methodGen.loadHandler());
287 il.append(methodGen.loadDOM());
288 il.append(methodGen.loadCurrentNode());
289
290 // Invoke BasisLibrary.startXslElemCheckQName()
291 il.append(new INVOKESTATIC(
292 cpg.addMethodref(BASIS_LIBRARY_CLASS, "startXslElement",
293 "(" + STRING_SIG
294 + STRING_SIG
295 + TRANSLET_OUTPUT_SIG
296 + DOM_INTF_SIG + "I)" + STRING_SIG)));
297
298
299 }
300
301 translateContents(classGen, methodGen);
302
303 if (!_ignore) {
304 il.append(methodGen.endElement());
305 }
306 }
307
308 /**
309 * Override this method to make sure that xsl:attributes are not
310 * copied to output if this xsl:element is to be ignored
311 */
312 public void translateContents(ClassGenerator classGen,
313 MethodGenerator methodGen) {
314 final int n = elementCount();
315 for (int i = 0; i < n; i++) {
316 final SyntaxTreeNode item =
317 (SyntaxTreeNode)getContents().elementAt(i);
318 if (_ignore && item instanceof XslAttribute) continue;
319 item.translate(classGen, methodGen);
320 }
321 }
322
323 }