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