1 package org.apache.velocity.runtime.parser.node;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with 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,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.io.BufferedReader;
23 import java.io.IOException;
24 import java.io.StringReader;
25 import java.io.StringWriter;
26
27 import org.apache.velocity.context.InternalContextAdapter;
28 import org.apache.velocity.exception.MethodInvocationException;
29 import org.apache.velocity.exception.ParseErrorException;
30 import org.apache.velocity.exception.ResourceNotFoundException;
31 import org.apache.velocity.exception.TemplateInitException;
32 import org.apache.velocity.runtime.RuntimeConstants;
33 import org.apache.velocity.runtime.parser.ParseException;
34 import org.apache.velocity.runtime.parser.Parser;
35 import org.apache.velocity.runtime.parser.ParserVisitor;
36
37 /**
38 * ASTStringLiteral support. Will interpolate!
39 *
40 * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
41 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
42 * @version $Id: ASTStringLiteral.java 471381 2006-11-05 08:56:58Z wglass $
43 */
44 public class ASTStringLiteral extends SimpleNode
45 {
46 /* cache the value of the interpolation switch */
47 private boolean interpolate = true;
48 private SimpleNode nodeTree = null;
49 private String image = "";
50 private String interpolateimage = "";
51
52 /** true if the string contains a line comment (##) */
53 private boolean containsLineComment;
54
55 /**
56 * @param id
57 */
58 public ASTStringLiteral(int id)
59 {
60 super(id);
61 }
62
63 /**
64 * @param p
65 * @param id
66 */
67 public ASTStringLiteral(Parser p, int id)
68 {
69 super(p, id);
70 }
71
72 /**
73 * init : we don't have to do much. Init the tree (there
74 * shouldn't be one) and then see if interpolation is turned on.
75 * @param context
76 * @param data
77 * @return Init result.
78 * @throws TemplateInitException
79 */
80 public Object init(InternalContextAdapter context, Object data)
81 throws TemplateInitException
82 {
83 /*
84 * simple habit... we prollie don't have an AST beneath us
85 */
86
87 super.init(context, data);
88
89 /*
90 * the stringlit is set at template parse time, so we can
91 * do this here for now. if things change and we can somehow
92 * create stringlits at runtime, this must
93 * move to the runtime execution path
94 *
95 * so, only if interpolation is turned on AND it starts
96 * with a " AND it has a directive or reference, then we
97 * can interpolate. Otherwise, don't bother.
98 */
99
100 interpolate = rsvc.getBoolean(RuntimeConstants.INTERPOLATE_STRINGLITERALS , true)
101 && getFirstToken().image.startsWith("\"")
102 && ((getFirstToken().image.indexOf('$') != -1)
103 || (getFirstToken().image.indexOf('#') != -1));
104
105 /*
106 * get the contents of the string, minus the '/" at each end
107 */
108
109 image = getFirstToken().image.substring(1,
110 getFirstToken().image.length() - 1);
111
112 /**
113 * note. A kludge on a kludge. The first part, Geir calls
114 * this the dreaded <MORE> kludge. Basically, the use of the
115 * <MORE> token eats the last character of an interpolated
116 * string. EXCEPT when a line comment (##) is in
117 * the string this isn't an issue.
118 *
119 * So, to solve this we look for a line comment. If it isn't found
120 * we add a space here and remove it later.
121 */
122
123 /**
124 * Note - this should really use a regexp to look for [^\]##
125 * but apparently escaping of line comments isn't working right
126 * now anyway.
127 */
128 containsLineComment = (image.indexOf("##") != -1);
129
130 /*
131 * if appropriate, tack a space on the end (dreaded <MORE> kludge)
132 */
133
134 if (!containsLineComment)
135 {
136 interpolateimage = image + " ";
137 }
138 else
139 {
140 interpolateimage = image;
141 }
142
143 if (interpolate)
144 {
145 /*
146 * now parse and init the nodeTree
147 */
148 BufferedReader br = new BufferedReader(new StringReader(interpolateimage));
149
150 /*
151 * it's possible to not have an initialization context - or we don't
152 * want to trust the caller - so have a fallback value if so
153 *
154 * Also, do *not* dump the VM namespace for this template
155 */
156
157 try
158 {
159 nodeTree = rsvc.parse(br, (context != null) ?
160 context.getCurrentTemplateName() : "StringLiteral", false);
161 }
162 catch (ParseException e)
163 {
164 throw new TemplateInitException("Problem parsing String literal.",
165 e,
166 (context != null) ? context.getCurrentTemplateName() : "StringLiteral",
167 getColumn(),
168 getLine() );
169 }
170
171 /*
172 * init with context. It won't modify anything
173 */
174
175 nodeTree.init(context, rsvc);
176 }
177
178 return data;
179 }
180
181 /**
182 * @see org.apache.velocity.runtime.parser.node.SimpleNode#jjtAccept(org.apache.velocity.runtime.parser.ParserVisitor, java.lang.Object)
183 */
184 public Object jjtAccept(ParserVisitor visitor, Object data)
185 {
186 return visitor.visit(this, data);
187 }
188
189 /**
190 * renders the value of the string literal
191 * If the properties allow, and the string literal contains a $ or a #
192 * the literal is rendered against the context
193 * Otherwise, the stringlit is returned.
194 * @param context
195 * @return result of the rendering.
196 */
197 public Object value(InternalContextAdapter context)
198 {
199 if (interpolate)
200 {
201 try
202 {
203 /*
204 * now render against the real context
205 */
206
207 StringWriter writer = new StringWriter();
208 nodeTree.render(context, writer);
209
210 /*
211 * and return the result as a String
212 */
213
214 String ret = writer.toString();
215
216 /*
217 * if appropriate, remove the space from the end
218 * (dreaded <MORE> kludge part deux)
219 */
220 if (!containsLineComment && ret.length() > 0)
221 {
222 return ret.substring(0, ret.length() - 1);
223 }
224 else
225 {
226 return ret;
227 }
228 }
229
230 /**
231 * For interpolated Strings we do not pass exceptions
232 * through -- just log the problem and move on.
233 */
234 catch( ParseErrorException e )
235 {
236 log.error("Error in interpolating string literal", e);
237 }
238 catch( MethodInvocationException e )
239 {
240 log.error("Error in interpolating string literal", e);
241 }
242 catch( ResourceNotFoundException e )
243 {
244 log.error("Error in interpolating string literal", e);
245 }
246
247 /**
248 * pass through application level runtime exceptions
249 */
250 catch( RuntimeException e )
251 {
252 throw e;
253 }
254
255 catch( IOException e )
256 {
257 log.error("Error in interpolating string literal", e);
258 }
259
260 }
261
262 /*
263 * ok, either not allowed to interpolate, there wasn't
264 * a ref or directive, or we failed, so
265 * just output the literal
266 */
267
268 return image;
269 }
270 }