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 }