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 }