View Javadoc

1   package org.apache.velocity.runtime.directive;
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.IOException;
23  import java.io.StringReader;
24  import java.io.Writer;
25  
26  import org.apache.velocity.context.EvaluateContext;
27  import org.apache.velocity.context.InternalContextAdapter;
28  import org.apache.velocity.context.InternalContextAdapterImpl;
29  import org.apache.velocity.exception.MethodInvocationException;
30  import org.apache.velocity.exception.ParseErrorException;
31  import org.apache.velocity.exception.ResourceNotFoundException;
32  import org.apache.velocity.exception.TemplateInitException;
33  import org.apache.velocity.runtime.RuntimeServices;
34  import org.apache.velocity.runtime.parser.ParseException;
35  import org.apache.velocity.runtime.parser.ParserTreeConstants;
36  import org.apache.velocity.runtime.parser.node.Node;
37  import org.apache.velocity.runtime.parser.node.SimpleNode;
38  import org.apache.velocity.util.introspection.Info;
39  
40  /**
41   * Evaluates the directive argument as a VTL string, using the existing
42   * context.
43   *
44   * @author <a href="mailto:wglass@apache.org">Will Glass-Husain</a>
45   * @version $Id: Evaluate.java 737544 2009-01-25 17:42:08Z nbubna $
46   * @since 1.6
47   */
48  public class Evaluate extends Directive
49  {
50  
51      /**
52       * Return name of this directive.
53       * @return The name of this directive.
54       */
55      public String getName()
56      {
57          return "evaluate";
58      }
59  
60      /**
61       * Return type of this directive.
62       * @return The type of this directive.
63       */
64      public int getType()
65      {
66          return LINE;
67      }
68  
69      /**
70       * Initialize and check arguments.
71       * @param rs
72       * @param context
73       * @param node
74       * @throws TemplateInitException
75       */
76      public void init(RuntimeServices rs, InternalContextAdapter context,
77                       Node node)
78          throws TemplateInitException
79      {
80          super.init( rs, context, node );
81  
82          /**
83           * Check that there is exactly one argument and it is a string or reference.
84           */  
85          
86          int argCount = node.jjtGetNumChildren();
87          if (argCount == 0)
88          {
89              throw new TemplateInitException(
90                      "#" + getName() + "() requires exactly one argument", 
91                      context.getCurrentTemplateName(),
92                      node.getColumn(),
93                      node.getLine());            
94          }
95          if (argCount > 1)
96          {
97              /* 
98               * use line/col of second argument
99               */
100             
101             throw new TemplateInitException(
102                     "#" + getName() + "() requires exactly one argument", 
103                     context.getCurrentTemplateName(),
104                     node.jjtGetChild(1).getColumn(),
105                     node.jjtGetChild(1).getLine());
106         }
107         
108         Node childNode = node.jjtGetChild(0);
109         if ( childNode.getType() !=  ParserTreeConstants.JJTSTRINGLITERAL &&
110              childNode.getType() !=  ParserTreeConstants.JJTREFERENCE )
111         {
112            throw new TemplateInitException(
113                    "#" + getName() + "()  argument must be a string literal or reference", 
114                    context.getCurrentTemplateName(),
115                    childNode.getColumn(),
116                    childNode.getLine());
117         }
118     }
119     
120     /**
121      * Evaluate the argument, convert to a String, and evaluate again 
122      * (with the same context).
123      * @param context
124      * @param writer
125      * @param node
126      * @return True if the directive rendered successfully.
127      * @throws IOException
128      * @throws ResourceNotFoundException
129      * @throws ParseErrorException 
130      * @throws MethodInvocationException
131      */
132     public boolean render(InternalContextAdapter context, Writer writer,
133             Node node) throws IOException, ResourceNotFoundException,
134             ParseErrorException, MethodInvocationException
135     {
136 
137         /*
138          * Evaluate the string with the current context.  We know there is
139          * exactly one argument and it is a string or reference.
140          */
141         
142         Object value = node.jjtGetChild(0).value( context );
143         String sourceText;
144         if ( value != null )
145         {
146             sourceText = value.toString();
147         } 
148         else
149         {
150             sourceText = "";
151         }
152         
153         /*
154          * The new string needs to be parsed since the text has been dynamically generated.
155          */
156         String templateName = context.getCurrentTemplateName();
157         SimpleNode nodeTree = null;
158 
159         try
160         {
161             nodeTree = rsvc.parse(new StringReader(sourceText), templateName, false);
162         }
163         catch (ParseException pex)
164         {
165             // use the line/column from the template
166             Info info = new Info( templateName, node.getLine(), node.getColumn() );
167             throw  new ParseErrorException( pex.getMessage(), info );
168         }
169         catch (TemplateInitException pex)
170         {
171             Info info = new Info( templateName, node.getLine(), node.getColumn() );
172             throw  new ParseErrorException( pex.getMessage(), info );
173         }
174 
175         /*
176          * now we want to init and render.  Chain the context
177          * to prevent any changes to the current context.
178          */
179 
180         if (nodeTree != null)
181         {
182             InternalContextAdapter ica = new EvaluateContext(context, rsvc);
183 
184             ica.pushCurrentTemplateName( templateName );
185 
186             try
187             {
188                 try
189                 {
190                     nodeTree.init( ica, rsvc );
191                 }
192                 catch (TemplateInitException pex)
193                 {
194                     Info info = new Info( templateName, node.getLine(), node.getColumn() );
195                     throw  new ParseErrorException( pex.getMessage(), info );
196                 }
197 
198                 try 
199                 {
200                     /*
201                      *  now render, and let any exceptions fly
202                      */
203                     nodeTree.render( ica, writer );
204                 }
205                 catch (ParseErrorException pex)
206                 {
207                     // convert any parsing errors to the correct line/col
208                     Info info = new Info( templateName, node.getLine(), node.getColumn() );
209                     throw  new ParseErrorException( pex.getMessage(), info );
210                 }
211             }
212             finally
213             {
214                 ica.popCurrentTemplateName();
215             }
216 
217             return true;
218         }
219 
220         
221         return false;
222     }
223 
224 }