View Javadoc

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 }