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: AttributeValueTemplate.java,v 1.10 2004/02/16 22:24:29 minchau Exp $
  18. */
  19. package com.sun.org.apache.xalan.internal.xsltc.compiler;
  20. import java.util.Enumeration;
  21. import java.util.Vector;
  22. import java.util.StringTokenizer;
  23. import java.util.NoSuchElementException;
  24. import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  25. import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
  26. import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
  27. import com.sun.org.apache.bcel.internal.generic.Instruction;
  28. import com.sun.org.apache.bcel.internal.generic.InstructionList;
  29. import com.sun.org.apache.bcel.internal.generic.NEW;
  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. /**
  36. * @author Jacek Ambroziak
  37. * @author Santiago Pericas-Geertsen
  38. */
  39. final class AttributeValueTemplate extends AttributeValue {
  40. final static int OUT_EXPR = 0;
  41. final static int IN_EXPR = 1;
  42. final static int IN_EXPR_SQUOTES = 2;
  43. final static int IN_EXPR_DQUOTES = 3;
  44. final static String DELIMITER = "\uFFFE"; // A Unicode nonchar
  45. public AttributeValueTemplate(String value, Parser parser,
  46. SyntaxTreeNode parent)
  47. {
  48. setParent(parent);
  49. setParser(parser);
  50. try {
  51. parseAVTemplate(value, parser);
  52. }
  53. catch (NoSuchElementException e) {
  54. reportError(parent, parser,
  55. ErrorMsg.ATTR_VAL_TEMPLATE_ERR, value);
  56. }
  57. }
  58. /**
  59. * Two-pass parsing of ATVs. In the first pass, double curly braces are
  60. * replaced by one, and expressions are delimited using DELIMITER. The
  61. * second pass splits up the resulting buffer into literal and non-literal
  62. * expressions. Errors are reported during the first pass.
  63. */
  64. private void parseAVTemplate(String text, Parser parser) {
  65. StringTokenizer tokenizer =
  66. new StringTokenizer(text, "{}\"\'", true);
  67. /*
  68. * First pass: replace double curly braces and delimit expressions
  69. * Simple automaton to parse ATVs, delimit expressions and report
  70. * errors.
  71. */
  72. String t = null;
  73. String lookahead = null;
  74. StringBuffer buffer = new StringBuffer();
  75. int state = OUT_EXPR;
  76. while (tokenizer.hasMoreTokens()) {
  77. // Use lookahead if available
  78. if (lookahead != null) {
  79. t = lookahead;
  80. lookahead = null;
  81. }
  82. else {
  83. t = tokenizer.nextToken();
  84. }
  85. if (t.length() == 1) {
  86. switch (t.charAt(0)) {
  87. case '{':
  88. switch (state) {
  89. case OUT_EXPR:
  90. lookahead = tokenizer.nextToken();
  91. if (lookahead.equals("{")) {
  92. buffer.append(lookahead); // replace {{ by {
  93. lookahead = null;
  94. }
  95. else {
  96. buffer.append(DELIMITER);
  97. state = IN_EXPR;
  98. }
  99. break;
  100. case IN_EXPR:
  101. case IN_EXPR_SQUOTES:
  102. case IN_EXPR_DQUOTES:
  103. reportError(getParent(), parser,
  104. ErrorMsg.ATTR_VAL_TEMPLATE_ERR, text);
  105. break;
  106. }
  107. break;
  108. case '}':
  109. switch (state) {
  110. case OUT_EXPR:
  111. lookahead = tokenizer.nextToken();
  112. if (lookahead.equals("}")) {
  113. buffer.append(lookahead); // replace }} by }
  114. lookahead = null;
  115. }
  116. else {
  117. reportError(getParent(), parser,
  118. ErrorMsg.ATTR_VAL_TEMPLATE_ERR, text);
  119. }
  120. break;
  121. case IN_EXPR:
  122. buffer.append(DELIMITER);
  123. state = OUT_EXPR;
  124. break;
  125. case IN_EXPR_SQUOTES:
  126. case IN_EXPR_DQUOTES:
  127. buffer.append(t);
  128. break;
  129. }
  130. break;
  131. case '\'':
  132. switch (state) {
  133. case IN_EXPR:
  134. state = IN_EXPR_SQUOTES;
  135. break;
  136. case IN_EXPR_SQUOTES:
  137. state = IN_EXPR;
  138. break;
  139. case OUT_EXPR:
  140. case IN_EXPR_DQUOTES:
  141. break;
  142. }
  143. buffer.append(t);
  144. break;
  145. case '\"':
  146. switch (state) {
  147. case IN_EXPR:
  148. state = IN_EXPR_DQUOTES;
  149. break;
  150. case IN_EXPR_DQUOTES:
  151. state = IN_EXPR;
  152. break;
  153. case OUT_EXPR:
  154. case IN_EXPR_SQUOTES:
  155. break;
  156. }
  157. buffer.append(t);
  158. break;
  159. default:
  160. buffer.append(t);
  161. break;
  162. }
  163. }
  164. else {
  165. buffer.append(t);
  166. }
  167. }
  168. // Must be in OUT_EXPR at the end of parsing
  169. if (state != OUT_EXPR) {
  170. reportError(getParent(), parser,
  171. ErrorMsg.ATTR_VAL_TEMPLATE_ERR, text);
  172. }
  173. /*
  174. * Second pass: split up buffer into literal and non-literal expressions.
  175. */
  176. tokenizer = new StringTokenizer(buffer.toString(), DELIMITER, true);
  177. while (tokenizer.hasMoreTokens()) {
  178. t = tokenizer.nextToken();
  179. if (t.equals(DELIMITER)) {
  180. addElement(parser.parseExpression(this, tokenizer.nextToken()));
  181. tokenizer.nextToken(); // consume other delimiter
  182. }
  183. else {
  184. addElement(new LiteralExpr(t));
  185. }
  186. }
  187. }
  188. public Type typeCheck(SymbolTable stable) throws TypeCheckError {
  189. final Vector contents = getContents();
  190. final int n = contents.size();
  191. for (int i = 0; i < n; i++) {
  192. final Expression exp = (Expression)contents.elementAt(i);
  193. if (!exp.typeCheck(stable).identicalTo(Type.String)) {
  194. contents.setElementAt(new CastExpr(exp, Type.String), i);
  195. }
  196. }
  197. return _type = Type.String;
  198. }
  199. public String toString() {
  200. final StringBuffer buffer = new StringBuffer("AVT:[");
  201. final int count = elementCount();
  202. for (int i = 0; i < count; i++) {
  203. buffer.append(elementAt(i).toString());
  204. if (i < count - 1)
  205. buffer.append(' ');
  206. }
  207. return buffer.append(']').toString();
  208. }
  209. public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
  210. if (elementCount() == 1) {
  211. final Expression exp = (Expression)elementAt(0);
  212. exp.translate(classGen, methodGen);
  213. }
  214. else {
  215. final ConstantPoolGen cpg = classGen.getConstantPool();
  216. final InstructionList il = methodGen.getInstructionList();
  217. final int initBuffer = cpg.addMethodref(STRING_BUFFER_CLASS,
  218. "<init>", "()V");
  219. final Instruction append =
  220. new INVOKEVIRTUAL(cpg.addMethodref(STRING_BUFFER_CLASS,
  221. "append",
  222. "(" + STRING_SIG + ")"
  223. + STRING_BUFFER_SIG));
  224. final int toString = cpg.addMethodref(STRING_BUFFER_CLASS,
  225. "toString",
  226. "()"+STRING_SIG);
  227. il.append(new NEW(cpg.addClass(STRING_BUFFER_CLASS)));
  228. il.append(DUP);
  229. il.append(new INVOKESPECIAL(initBuffer));
  230. // StringBuffer is on the stack
  231. final Enumeration elements = elements();
  232. while (elements.hasMoreElements()) {
  233. final Expression exp = (Expression)elements.nextElement();
  234. exp.translate(classGen, methodGen);
  235. il.append(append);
  236. }
  237. il.append(new INVOKEVIRTUAL(toString));
  238. }
  239. }
  240. }