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 }