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: AttributeValueTemplate.java 468650 2006-10-28 07:03:30Z minchau $
020 */
021
022 package org.apache.xalan.xsltc.compiler;
023
024 import java.util.Enumeration;
025 import java.util.Vector;
026 import java.util.StringTokenizer;
027 import java.util.NoSuchElementException;
028
029 import org.apache.bcel.generic.ConstantPoolGen;
030 import org.apache.bcel.generic.INVOKESPECIAL;
031 import org.apache.bcel.generic.INVOKEVIRTUAL;
032 import org.apache.bcel.generic.Instruction;
033 import org.apache.bcel.generic.InstructionList;
034 import org.apache.bcel.generic.NEW;
035 import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
036 import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
037 import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
038 import org.apache.xalan.xsltc.compiler.util.Type;
039 import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
040
041 /**
042 * @author Jacek Ambroziak
043 * @author Santiago Pericas-Geertsen
044 */
045 final class AttributeValueTemplate extends AttributeValue {
046
047 final static int OUT_EXPR = 0;
048 final static int IN_EXPR = 1;
049 final static int IN_EXPR_SQUOTES = 2;
050 final static int IN_EXPR_DQUOTES = 3;
051 final static String DELIMITER = "\uFFFE"; // A Unicode nonchar
052
053 public AttributeValueTemplate(String value, Parser parser,
054 SyntaxTreeNode parent)
055 {
056 setParent(parent);
057 setParser(parser);
058
059 try {
060 parseAVTemplate(value, parser);
061 }
062 catch (NoSuchElementException e) {
063 reportError(parent, parser,
064 ErrorMsg.ATTR_VAL_TEMPLATE_ERR, value);
065 }
066 }
067
068 /**
069 * Two-pass parsing of ATVs. In the first pass, double curly braces are
070 * replaced by one, and expressions are delimited using DELIMITER. The
071 * second pass splits up the resulting buffer into literal and non-literal
072 * expressions. Errors are reported during the first pass.
073 */
074 private void parseAVTemplate(String text, Parser parser) {
075 StringTokenizer tokenizer =
076 new StringTokenizer(text, "{}\"\'", true);
077
078 /*
079 * First pass: replace double curly braces and delimit expressions
080 * Simple automaton to parse ATVs, delimit expressions and report
081 * errors.
082 */
083 String t = null;
084 String lookahead = null;
085 StringBuffer buffer = new StringBuffer();
086 int state = OUT_EXPR;
087
088 while (tokenizer.hasMoreTokens()) {
089 // Use lookahead if available
090 if (lookahead != null) {
091 t = lookahead;
092 lookahead = null;
093 }
094 else {
095 t = tokenizer.nextToken();
096 }
097
098 if (t.length() == 1) {
099 switch (t.charAt(0)) {
100 case '{':
101 switch (state) {
102 case OUT_EXPR:
103 lookahead = tokenizer.nextToken();
104 if (lookahead.equals("{")) {
105 buffer.append(lookahead); // replace {{ by {
106 lookahead = null;
107 }
108 else {
109 buffer.append(DELIMITER);
110 state = IN_EXPR;
111 }
112 break;
113 case IN_EXPR:
114 case IN_EXPR_SQUOTES:
115 case IN_EXPR_DQUOTES:
116 reportError(getParent(), parser,
117 ErrorMsg.ATTR_VAL_TEMPLATE_ERR, text);
118 break;
119 }
120 break;
121 case '}':
122 switch (state) {
123 case OUT_EXPR:
124 lookahead = tokenizer.nextToken();
125 if (lookahead.equals("}")) {
126 buffer.append(lookahead); // replace }} by }
127 lookahead = null;
128 }
129 else {
130 reportError(getParent(), parser,
131 ErrorMsg.ATTR_VAL_TEMPLATE_ERR, text);
132 }
133 break;
134 case IN_EXPR:
135 buffer.append(DELIMITER);
136 state = OUT_EXPR;
137 break;
138 case IN_EXPR_SQUOTES:
139 case IN_EXPR_DQUOTES:
140 buffer.append(t);
141 break;
142 }
143 break;
144 case '\'':
145 switch (state) {
146 case IN_EXPR:
147 state = IN_EXPR_SQUOTES;
148 break;
149 case IN_EXPR_SQUOTES:
150 state = IN_EXPR;
151 break;
152 case OUT_EXPR:
153 case IN_EXPR_DQUOTES:
154 break;
155 }
156 buffer.append(t);
157 break;
158 case '\"':
159 switch (state) {
160 case IN_EXPR:
161 state = IN_EXPR_DQUOTES;
162 break;
163 case IN_EXPR_DQUOTES:
164 state = IN_EXPR;
165 break;
166 case OUT_EXPR:
167 case IN_EXPR_SQUOTES:
168 break;
169 }
170 buffer.append(t);
171 break;
172 default:
173 buffer.append(t);
174 break;
175 }
176 }
177 else {
178 buffer.append(t);
179 }
180 }
181
182 // Must be in OUT_EXPR at the end of parsing
183 if (state != OUT_EXPR) {
184 reportError(getParent(), parser,
185 ErrorMsg.ATTR_VAL_TEMPLATE_ERR, text);
186 }
187
188 /*
189 * Second pass: split up buffer into literal and non-literal expressions.
190 */
191 tokenizer = new StringTokenizer(buffer.toString(), DELIMITER, true);
192
193 while (tokenizer.hasMoreTokens()) {
194 t = tokenizer.nextToken();
195
196 if (t.equals(DELIMITER)) {
197 addElement(parser.parseExpression(this, tokenizer.nextToken()));
198 tokenizer.nextToken(); // consume other delimiter
199 }
200 else {
201 addElement(new LiteralExpr(t));
202 }
203 }
204 }
205
206 public Type typeCheck(SymbolTable stable) throws TypeCheckError {
207 final Vector contents = getContents();
208 final int n = contents.size();
209 for (int i = 0; i < n; i++) {
210 final Expression exp = (Expression)contents.elementAt(i);
211 if (!exp.typeCheck(stable).identicalTo(Type.String)) {
212 contents.setElementAt(new CastExpr(exp, Type.String), i);
213 }
214 }
215 return _type = Type.String;
216 }
217
218 public String toString() {
219 final StringBuffer buffer = new StringBuffer("AVT:[");
220 final int count = elementCount();
221 for (int i = 0; i < count; i++) {
222 buffer.append(elementAt(i).toString());
223 if (i < count - 1)
224 buffer.append(' ');
225 }
226 return buffer.append(']').toString();
227 }
228
229 public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
230 if (elementCount() == 1) {
231 final Expression exp = (Expression)elementAt(0);
232 exp.translate(classGen, methodGen);
233 }
234 else {
235 final ConstantPoolGen cpg = classGen.getConstantPool();
236 final InstructionList il = methodGen.getInstructionList();
237 final int initBuffer = cpg.addMethodref(STRING_BUFFER_CLASS,
238 "<init>", "()V");
239 final Instruction append =
240 new INVOKEVIRTUAL(cpg.addMethodref(STRING_BUFFER_CLASS,
241 "append",
242 "(" + STRING_SIG + ")"
243 + STRING_BUFFER_SIG));
244
245 final int toString = cpg.addMethodref(STRING_BUFFER_CLASS,
246 "toString",
247 "()"+STRING_SIG);
248 il.append(new NEW(cpg.addClass(STRING_BUFFER_CLASS)));
249 il.append(DUP);
250 il.append(new INVOKESPECIAL(initBuffer));
251 // StringBuffer is on the stack
252 final Enumeration elements = elements();
253 while (elements.hasMoreElements()) {
254 final Expression exp = (Expression)elements.nextElement();
255 exp.translate(classGen, methodGen);
256 il.append(append);
257 }
258 il.append(new INVOKEVIRTUAL(toString));
259 }
260 }
261 }