001: package org.apache.velocity.runtime.parser.node;
002:
003: /*
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: import java.io.BufferedReader;
023: import java.io.IOException;
024: import java.io.StringReader;
025: import java.io.StringWriter;
026:
027: import org.apache.velocity.context.InternalContextAdapter;
028: import org.apache.velocity.exception.MethodInvocationException;
029: import org.apache.velocity.exception.ParseErrorException;
030: import org.apache.velocity.exception.ResourceNotFoundException;
031: import org.apache.velocity.exception.TemplateInitException;
032: import org.apache.velocity.runtime.RuntimeConstants;
033: import org.apache.velocity.runtime.parser.ParseException;
034: import org.apache.velocity.runtime.parser.Parser;
035: import org.apache.velocity.runtime.parser.ParserVisitor;
036:
037: /**
038: * ASTStringLiteral support. Will interpolate!
039: *
040: * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
041: * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
042: * @version $Id: ASTStringLiteral.java 471381 2006-11-05 08:56:58Z wglass $
043: */
044: public class ASTStringLiteral extends SimpleNode {
045: /* cache the value of the interpolation switch */
046: private boolean interpolate = true;
047: private SimpleNode nodeTree = null;
048: private String image = "";
049: private String interpolateimage = "";
050:
051: /** true if the string contains a line comment (##) */
052: private boolean containsLineComment;
053:
054: /**
055: * @param id
056: */
057: public ASTStringLiteral(int id) {
058: super (id);
059: }
060:
061: /**
062: * @param p
063: * @param id
064: */
065: public ASTStringLiteral(Parser p, int id) {
066: super (p, id);
067: }
068:
069: /**
070: * init : we don't have to do much. Init the tree (there
071: * shouldn't be one) and then see if interpolation is turned on.
072: * @param context
073: * @param data
074: * @return Init result.
075: * @throws TemplateInitException
076: */
077: public Object init(InternalContextAdapter context, Object data)
078: throws TemplateInitException {
079: /*
080: * simple habit... we prollie don't have an AST beneath us
081: */
082:
083: super .init(context, data);
084:
085: /*
086: * the stringlit is set at template parse time, so we can
087: * do this here for now. if things change and we can somehow
088: * create stringlits at runtime, this must
089: * move to the runtime execution path
090: *
091: * so, only if interpolation is turned on AND it starts
092: * with a " AND it has a directive or reference, then we
093: * can interpolate. Otherwise, don't bother.
094: */
095:
096: interpolate = rsvc.getBoolean(
097: RuntimeConstants.INTERPOLATE_STRINGLITERALS, true)
098: && getFirstToken().image.startsWith("\"")
099: && ((getFirstToken().image.indexOf('$') != -1) || (getFirstToken().image
100: .indexOf('#') != -1));
101:
102: /*
103: * get the contents of the string, minus the '/" at each end
104: */
105:
106: image = getFirstToken().image.substring(1,
107: getFirstToken().image.length() - 1);
108:
109: /**
110: * note. A kludge on a kludge. The first part, Geir calls
111: * this the dreaded <MORE> kludge. Basically, the use of the
112: * <MORE> token eats the last character of an interpolated
113: * string. EXCEPT when a line comment (##) is in
114: * the string this isn't an issue.
115: *
116: * So, to solve this we look for a line comment. If it isn't found
117: * we add a space here and remove it later.
118: */
119:
120: /**
121: * Note - this should really use a regexp to look for [^\]##
122: * but apparently escaping of line comments isn't working right
123: * now anyway.
124: */
125: containsLineComment = (image.indexOf("##") != -1);
126:
127: /*
128: * if appropriate, tack a space on the end (dreaded <MORE> kludge)
129: */
130:
131: if (!containsLineComment) {
132: interpolateimage = image + " ";
133: } else {
134: interpolateimage = image;
135: }
136:
137: if (interpolate) {
138: /*
139: * now parse and init the nodeTree
140: */
141: BufferedReader br = new BufferedReader(new StringReader(
142: interpolateimage));
143:
144: /*
145: * it's possible to not have an initialization context - or we don't
146: * want to trust the caller - so have a fallback value if so
147: *
148: * Also, do *not* dump the VM namespace for this template
149: */
150:
151: try {
152: nodeTree = rsvc.parse(br, (context != null) ? context
153: .getCurrentTemplateName() : "StringLiteral",
154: false);
155: } catch (ParseException e) {
156: throw new TemplateInitException(
157: "Problem parsing String literal.", e,
158: (context != null) ? context
159: .getCurrentTemplateName()
160: : "StringLiteral", getColumn(),
161: getLine());
162: }
163:
164: /*
165: * init with context. It won't modify anything
166: */
167:
168: nodeTree.init(context, rsvc);
169: }
170:
171: return data;
172: }
173:
174: /**
175: * @see org.apache.velocity.runtime.parser.node.SimpleNode#jjtAccept(org.apache.velocity.runtime.parser.ParserVisitor, java.lang.Object)
176: */
177: public Object jjtAccept(ParserVisitor visitor, Object data) {
178: return visitor.visit(this , data);
179: }
180:
181: /**
182: * renders the value of the string literal
183: * If the properties allow, and the string literal contains a $ or a #
184: * the literal is rendered against the context
185: * Otherwise, the stringlit is returned.
186: * @param context
187: * @return result of the rendering.
188: */
189: public Object value(InternalContextAdapter context) {
190: if (interpolate) {
191: try {
192: /*
193: * now render against the real context
194: */
195:
196: StringWriter writer = new StringWriter();
197: nodeTree.render(context, writer);
198:
199: /*
200: * and return the result as a String
201: */
202:
203: String ret = writer.toString();
204:
205: /*
206: * if appropriate, remove the space from the end
207: * (dreaded <MORE> kludge part deux)
208: */
209: if (!containsLineComment && ret.length() > 0) {
210: return ret.substring(0, ret.length() - 1);
211: } else {
212: return ret;
213: }
214: }
215:
216: /**
217: * For interpolated Strings we do not pass exceptions
218: * through -- just log the problem and move on.
219: */
220: catch (ParseErrorException e) {
221: log.error("Error in interpolating string literal", e);
222: } catch (MethodInvocationException e) {
223: log.error("Error in interpolating string literal", e);
224: } catch (ResourceNotFoundException e) {
225: log.error("Error in interpolating string literal", e);
226: }
227:
228: /**
229: * pass through application level runtime exceptions
230: */
231: catch (RuntimeException e) {
232: throw e;
233: }
234:
235: catch (IOException e) {
236: log.error("Error in interpolating string literal", e);
237: }
238:
239: }
240:
241: /*
242: * ok, either not allowed to interpolate, there wasn't
243: * a ref or directive, or we failed, so
244: * just output the literal
245: */
246:
247: return image;
248: }
249: }
|