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