1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 */ 4 /* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 /* 21 * $Id: AttributeValueTemplate.java,v 1.2.4.1 2005/09/01 10:26:57 pvedula Exp $ 22 */ 23 24 package com.sun.org.apache.xalan.internal.xsltc.compiler; 25 26 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen; 27 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL; 28 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL; 29 import com.sun.org.apache.bcel.internal.generic.Instruction; 30 import com.sun.org.apache.bcel.internal.generic.InstructionList; 31 import com.sun.org.apache.bcel.internal.generic.NEW; 32 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator; 33 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg; 34 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator; 35 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type; 36 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError; 37 import java.util.Iterator; 38 import java.util.List; 39 import java.util.NoSuchElementException; 40 import java.util.StringTokenizer; 41 42 /** 43 * @author Jacek Ambroziak 44 * @author Santiago Pericas-Geertsen 45 */ 46 final class AttributeValueTemplate extends AttributeValue { 47 48 final static int OUT_EXPR = 0; 49 final static int IN_EXPR = 1; 50 final static int IN_EXPR_SQUOTES = 2; 51 final static int IN_EXPR_DQUOTES = 3; 52 final static String DELIMITER = "\uFFFE"; // A Unicode nonchar 53 AttributeValueTemplate(String value, Parser parser, SyntaxTreeNode parent)54 public AttributeValueTemplate(String value, Parser parser, 55 SyntaxTreeNode parent) 56 { 57 setParent(parent); 58 setParser(parser); 59 60 try { 61 parseAVTemplate(value, parser); 62 } 63 catch (NoSuchElementException e) { 64 reportError(parent, parser, 65 ErrorMsg.ATTR_VAL_TEMPLATE_ERR, value); 66 } 67 } 68 69 /** 70 * Two-pass parsing of ATVs. In the first pass, double curly braces are 71 * replaced by one, and expressions are delimited using DELIMITER. The 72 * second pass splits up the resulting buffer into literal and non-literal 73 * expressions. Errors are reported during the first pass. 74 */ parseAVTemplate(String text, Parser parser)75 private void parseAVTemplate(String text, Parser parser) { 76 StringTokenizer tokenizer = 77 new StringTokenizer(text, "{}\"\'", true); 78 79 /* 80 * First pass: replace double curly braces and delimit expressions 81 * Simple automaton to parse ATVs, delimit expressions and report 82 * errors. 83 */ 84 String t = null; 85 String lookahead = null; 86 StringBuffer buffer = new StringBuffer(); 87 int state = OUT_EXPR; 88 89 while (tokenizer.hasMoreTokens()) { 90 // Use lookahead if available 91 if (lookahead != null) { 92 t = lookahead; 93 lookahead = null; 94 } 95 else { 96 t = tokenizer.nextToken(); 97 } 98 99 if (t.length() == 1) { 100 switch (t.charAt(0)) { 101 case '{': 102 switch (state) { 103 case OUT_EXPR: 104 lookahead = tokenizer.nextToken(); 105 if (lookahead.equals("{")) { 106 buffer.append(lookahead); // replace {{ by { 107 lookahead = null; 108 } 109 else { 110 buffer.append(DELIMITER); 111 state = IN_EXPR; 112 } 113 break; 114 case IN_EXPR: 115 case IN_EXPR_SQUOTES: 116 case IN_EXPR_DQUOTES: 117 reportError(getParent(), parser, 118 ErrorMsg.ATTR_VAL_TEMPLATE_ERR, text); 119 break; 120 } 121 break; 122 case '}': 123 switch (state) { 124 case OUT_EXPR: 125 lookahead = tokenizer.nextToken(); 126 if (lookahead.equals("}")) { 127 buffer.append(lookahead); // replace }} by } 128 lookahead = null; 129 } 130 else { 131 reportError(getParent(), parser, 132 ErrorMsg.ATTR_VAL_TEMPLATE_ERR, text); 133 } 134 break; 135 case IN_EXPR: 136 buffer.append(DELIMITER); 137 state = OUT_EXPR; 138 break; 139 case IN_EXPR_SQUOTES: 140 case IN_EXPR_DQUOTES: 141 buffer.append(t); 142 break; 143 } 144 break; 145 case '\'': 146 switch (state) { 147 case IN_EXPR: 148 state = IN_EXPR_SQUOTES; 149 break; 150 case IN_EXPR_SQUOTES: 151 state = IN_EXPR; 152 break; 153 case OUT_EXPR: 154 case IN_EXPR_DQUOTES: 155 break; 156 } 157 buffer.append(t); 158 break; 159 case '\"': 160 switch (state) { 161 case IN_EXPR: 162 state = IN_EXPR_DQUOTES; 163 break; 164 case IN_EXPR_DQUOTES: 165 state = IN_EXPR; 166 break; 167 case OUT_EXPR: 168 case IN_EXPR_SQUOTES: 169 break; 170 } 171 buffer.append(t); 172 break; 173 default: 174 buffer.append(t); 175 break; 176 } 177 } 178 else { 179 buffer.append(t); 180 } 181 } 182 183 // Must be in OUT_EXPR at the end of parsing 184 if (state != OUT_EXPR) { 185 reportError(getParent(), parser, 186 ErrorMsg.ATTR_VAL_TEMPLATE_ERR, text); 187 } 188 189 /* 190 * Second pass: split up buffer into literal and non-literal expressions. 191 */ 192 tokenizer = new StringTokenizer(buffer.toString(), DELIMITER, true); 193 194 while (tokenizer.hasMoreTokens()) { 195 t = tokenizer.nextToken(); 196 197 if (t.equals(DELIMITER)) { 198 addElement(parser.parseExpression(this, tokenizer.nextToken())); 199 tokenizer.nextToken(); // consume other delimiter 200 } 201 else { 202 addElement(new LiteralExpr(t)); 203 } 204 } 205 } 206 typeCheck(SymbolTable stable)207 public Type typeCheck(SymbolTable stable) throws TypeCheckError { 208 final List<SyntaxTreeNode> contents = getContents(); 209 final int n = contents.size(); 210 for (int i = 0; i < n; i++) { 211 final Expression exp = (Expression)contents.get(i); 212 if (!exp.typeCheck(stable).identicalTo(Type.String)) { 213 contents.set(i, new CastExpr(exp, Type.String)); 214 } 215 } 216 return _type = Type.String; 217 } 218 toString()219 public String toString() { 220 final StringBuffer buffer = new StringBuffer("AVT:["); 221 final int count = elementCount(); 222 for (int i = 0; i < count; i++) { 223 buffer.append(elementAt(i).toString()); 224 if (i < count - 1) 225 buffer.append(' '); 226 } 227 return buffer.append(']').toString(); 228 } 229 translate(ClassGenerator classGen, MethodGenerator methodGen)230 public void translate(ClassGenerator classGen, MethodGenerator methodGen) { 231 if (elementCount() == 1) { 232 final Expression exp = (Expression)elementAt(0); 233 exp.translate(classGen, methodGen); 234 } 235 else { 236 final ConstantPoolGen cpg = classGen.getConstantPool(); 237 final InstructionList il = methodGen.getInstructionList(); 238 final int initBuffer = cpg.addMethodref(STRING_BUFFER_CLASS, 239 "<init>", "()V"); 240 final Instruction append = 241 new INVOKEVIRTUAL(cpg.addMethodref(STRING_BUFFER_CLASS, 242 "append", 243 "(" + STRING_SIG + ")" 244 + STRING_BUFFER_SIG)); 245 246 final int toString = cpg.addMethodref(STRING_BUFFER_CLASS, 247 "toString", 248 "()"+STRING_SIG); 249 il.append(new NEW(cpg.addClass(STRING_BUFFER_CLASS))); 250 il.append(DUP); 251 il.append(new INVOKESPECIAL(initBuffer)); 252 // StringBuffer is on the stack 253 final Iterator<SyntaxTreeNode> elements = elements(); 254 while (elements.hasNext()) { 255 final Expression exp = (Expression)elements.next(); 256 exp.translate(classGen, methodGen); 257 il.append(append); 258 } 259 il.append(new INVOKEVIRTUAL(toString)); 260 } 261 } 262 } 263