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