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