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