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: XslAttribute.java,v 1.24 2004/02/24 03:55:48 zongaro Exp $
  18. */
  19. package com.sun.org.apache.xalan.internal.xsltc.compiler;
  20. import java.util.Vector;
  21. import com.sun.org.apache.bcel.internal.generic.ALOAD;
  22. import com.sun.org.apache.bcel.internal.generic.ASTORE;
  23. import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  24. import com.sun.org.apache.bcel.internal.generic.GETFIELD;
  25. import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
  26. import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
  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.XMLChar;
  37. import com.sun.org.apache.xml.internal.serializer.ElemDesc;
  38. import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
  39. /**
  40. * @author Jacek Ambroziak
  41. * @author Santiago Pericas-Geertsen
  42. * @author Morten Jorgensen
  43. * @author Erwin Bolwidt <ejb@klomp.org>
  44. * @author Gunnlaugur Briem <gthb@dimon.is>
  45. */
  46. final class XslAttribute extends Instruction {
  47. private String _prefix;
  48. private AttributeValue _name; // name treated as AVT (7.1.3)
  49. private AttributeValueTemplate _namespace = null;
  50. private boolean _ignore = false;
  51. private boolean _isLiteral = false; // specified name is not AVT
  52. /**
  53. * Returns the name of the attribute
  54. */
  55. public AttributeValue getName() {
  56. return _name;
  57. }
  58. /**
  59. * Displays the contents of the attribute
  60. */
  61. public void display(int indent) {
  62. indent(indent);
  63. Util.println("Attribute " + _name);
  64. displayContents(indent + IndentIncrement);
  65. }
  66. /**
  67. * Parses the attribute's contents. Special care taken for namespaces.
  68. */
  69. public void parseContents(Parser parser) {
  70. boolean generated = false;
  71. final SymbolTable stable = parser.getSymbolTable();
  72. String name = getAttribute("name");
  73. String namespace = getAttribute("namespace");
  74. QName qname = parser.getQName(name, false);
  75. final String prefix = qname.getPrefix();
  76. if (((prefix != null) && (prefix.equals(XMLNS_PREFIX)))||(name.equals(XMLNS_PREFIX))) {
  77. reportError(this, parser, ErrorMsg.ILLEGAL_ATTR_NAME_ERR, name);
  78. return;
  79. }
  80. _isLiteral = Util.isLiteral(name);
  81. if (_isLiteral) {
  82. if (!XMLChar.isValidQName(name)) {
  83. reportError(this, parser, ErrorMsg.ILLEGAL_ATTR_NAME_ERR, name);
  84. return;
  85. }
  86. }
  87. // Ignore attribute if preceeded by some other type of element
  88. final SyntaxTreeNode parent = getParent();
  89. final Vector siblings = parent.getContents();
  90. for (int i = 0; i < parent.elementCount(); i++) {
  91. SyntaxTreeNode item = (SyntaxTreeNode)siblings.elementAt(i);
  92. if (item == this) break;
  93. // These three objects result in one or more attribute output
  94. if (item instanceof XslAttribute) continue;
  95. if (item instanceof UseAttributeSets) continue;
  96. if (item instanceof LiteralAttribute) continue;
  97. if (item instanceof Text) continue;
  98. // These objects _can_ result in one or more attribute
  99. // The output handler will generate an error if not (at runtime)
  100. if (item instanceof If) continue;
  101. if (item instanceof Choose) continue;
  102. if (item instanceof CopyOf) continue;
  103. if (item instanceof VariableBase) continue;
  104. // Report warning but do not ignore attribute
  105. reportWarning(this, parser, ErrorMsg.STRAY_ATTRIBUTE_ERR, name);
  106. }
  107. // Get namespace from namespace attribute?
  108. if (namespace != null && namespace != Constants.EMPTYSTRING) {
  109. _prefix = lookupPrefix(namespace);
  110. _namespace = new AttributeValueTemplate(namespace, parser, this);
  111. }
  112. // Get namespace from prefix in name attribute?
  113. else if (prefix != null && prefix != Constants.EMPTYSTRING) {
  114. _prefix = prefix;
  115. namespace = lookupNamespace(prefix);
  116. if (namespace != null) {
  117. _namespace = new AttributeValueTemplate(namespace, parser, this);
  118. }
  119. }
  120. // Common handling for namespaces:
  121. if (_namespace != null) {
  122. // Generate prefix if we have none
  123. if (_prefix == null || _prefix == Constants.EMPTYSTRING) {
  124. if (prefix != null) {
  125. _prefix = prefix;
  126. }
  127. else {
  128. _prefix = stable.generateNamespacePrefix();
  129. generated = true;
  130. }
  131. }
  132. else if (prefix != null && !prefix.equals(_prefix)) {
  133. _prefix = prefix;
  134. }
  135. name = _prefix + ":" + qname.getLocalPart();
  136. /*
  137. * TODO: The namespace URI must be passed to the parent
  138. * element but we don't yet know what the actual URI is
  139. * (as we only know it as an attribute value template).
  140. */
  141. if ((parent instanceof LiteralElement) && (!generated)) {
  142. ((LiteralElement)parent).registerNamespace(_prefix,
  143. namespace,
  144. stable, false);
  145. }
  146. }
  147. if (parent instanceof LiteralElement) {
  148. ((LiteralElement)parent).addAttribute(this);
  149. }
  150. _name = AttributeValue.create(this, name, parser);
  151. parseChildren(parser);
  152. }
  153. public Type typeCheck(SymbolTable stable) throws TypeCheckError {
  154. if (!_ignore) {
  155. _name.typeCheck(stable);
  156. if (_namespace != null) {
  157. _namespace.typeCheck(stable);
  158. }
  159. typeCheckContents(stable);
  160. }
  161. return Type.Void;
  162. }
  163. /**
  164. *
  165. */
  166. public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
  167. final ConstantPoolGen cpg = classGen.getConstantPool();
  168. final InstructionList il = methodGen.getInstructionList();
  169. if (_ignore) return;
  170. _ignore = true;
  171. // Compile code that emits any needed namespace declaration
  172. if (_namespace != null) {
  173. // public void attribute(final String name, final String value)
  174. il.append(methodGen.loadHandler());
  175. il.append(new PUSH(cpg,_prefix));
  176. _namespace.translate(classGen,methodGen);
  177. il.append(methodGen.namespace());
  178. }
  179. if (!_isLiteral) {
  180. // if the qname is an AVT, then the qname has to be checked at runtime if it is a valid qname
  181. LocalVariableGen nameValue = methodGen.addLocalVariable2("nameValue",
  182. Util.getJCRefType(STRING_SIG),
  183. il.getEnd());
  184. // store the name into a variable first so _name.translate only needs to be called once
  185. _name.translate(classGen, methodGen);
  186. il.append(new ASTORE(nameValue.getIndex()));
  187. il.append(new ALOAD(nameValue.getIndex()));
  188. // call checkQName if the name is an AVT
  189. final int check = cpg.addMethodref(BASIS_LIBRARY_CLASS, "checkAttribQName",
  190. "("
  191. +STRING_SIG
  192. +")V");
  193. il.append(new INVOKESTATIC(check));
  194. // Save the current handler base on the stack
  195. il.append(methodGen.loadHandler());
  196. il.append(DUP); // first arg to "attributes" call
  197. // load name value again
  198. il.append(new ALOAD(nameValue.getIndex()));
  199. } else {
  200. // Save the current handler base on the stack
  201. il.append(methodGen.loadHandler());
  202. il.append(DUP); // first arg to "attributes" call
  203. // Push attribute name
  204. _name.translate(classGen, methodGen);// 2nd arg
  205. }
  206. // Push attribute value - shortcut for literal strings
  207. if ((elementCount() == 1) && (elementAt(0) instanceof Text)) {
  208. il.append(new PUSH(cpg, ((Text)elementAt(0)).getText()));
  209. }
  210. else {
  211. il.append(classGen.loadTranslet());
  212. il.append(new GETFIELD(cpg.addFieldref(TRANSLET_CLASS,
  213. "stringValueHandler",
  214. STRING_VALUE_HANDLER_SIG)));
  215. il.append(DUP);
  216. il.append(methodGen.storeHandler());
  217. // translate contents with substituted handler
  218. translateContents(classGen, methodGen);
  219. // get String out of the handler
  220. il.append(new INVOKEVIRTUAL(cpg.addMethodref(STRING_VALUE_HANDLER,
  221. "getValue",
  222. "()" + STRING_SIG)));
  223. }
  224. SyntaxTreeNode parent = getParent();
  225. if (parent instanceof LiteralElement
  226. && ((LiteralElement)parent).allAttributesUnique()) {
  227. int flags = 0;
  228. ElemDesc elemDesc = ((LiteralElement)parent).getElemDesc();
  229. // Set the HTML flags
  230. if (elemDesc != null && _name instanceof SimpleAttributeValue) {
  231. String attrName = ((SimpleAttributeValue)_name).toString();
  232. if (elemDesc.isAttrFlagSet(attrName, ElemDesc.ATTREMPTY)) {
  233. flags = flags | SerializationHandler.HTML_ATTREMPTY;
  234. }
  235. else if (elemDesc.isAttrFlagSet(attrName, ElemDesc.ATTRURL)) {
  236. flags = flags | SerializationHandler.HTML_ATTRURL;
  237. }
  238. }
  239. il.append(new PUSH(cpg, flags));
  240. il.append(methodGen.uniqueAttribute());
  241. }
  242. else {
  243. // call "attribute"
  244. il.append(methodGen.attribute());
  245. }
  246. // Restore old handler base from stack
  247. il.append(methodGen.storeHandler());
  248. }
  249. }