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