1 package org.apache.velocity.runtime.directive;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.IOException;
23 import java.io.Writer;
24 import java.util.ArrayList;
25 import java.util.Iterator;
26
27 import org.apache.velocity.context.InternalContextAdapter;
28 import org.apache.velocity.exception.TemplateInitException;
29 import org.apache.velocity.exception.VelocityException;
30 import org.apache.velocity.runtime.RuntimeConstants;
31 import org.apache.velocity.runtime.RuntimeServices;
32 import org.apache.velocity.runtime.log.Log;
33 import org.apache.velocity.runtime.parser.ParseException;
34 import org.apache.velocity.runtime.parser.ParserTreeConstants;
35 import org.apache.velocity.runtime.parser.Token;
36 import org.apache.velocity.runtime.parser.node.ASTReference;
37 import org.apache.velocity.runtime.parser.node.Node;
38 import org.apache.velocity.runtime.parser.node.SimpleNode;
39 import org.apache.velocity.util.introspection.Info;
40
41
42
43
44
45
46
47
48
49
50 public class Foreach extends Directive
51 {
52
53
54
55
56 public String getName()
57 {
58 return "foreach";
59 }
60
61
62
63
64
65 public int getType()
66 {
67 return BLOCK;
68 }
69
70
71
72
73 private int maxNbrLoops;
74
75
76
77
78 private boolean skipInvalidIterator;
79
80
81
82
83
84
85
86
87
88
89
90 private String elementKey;
91
92
93
94
95 protected Info uberInfo;
96
97
98
99
100
101
102
103
104
105 public void init(RuntimeServices rs, InternalContextAdapter context, Node node)
106 throws TemplateInitException
107 {
108 super.init(rs, context, node);
109
110 maxNbrLoops = rsvc.getInt(RuntimeConstants.MAX_NUMBER_LOOPS,
111 Integer.MAX_VALUE);
112 if (maxNbrLoops < 1)
113 {
114 maxNbrLoops = Integer.MAX_VALUE;
115 }
116 skipInvalidIterator =
117 rsvc.getBoolean(RuntimeConstants.SKIP_INVALID_ITERATOR, true);
118
119 if (rsvc.getBoolean(RuntimeConstants.RUNTIME_REFERENCES_STRICT, false))
120 {
121
122
123 skipInvalidIterator = rsvc.getBoolean(RuntimeConstants.SKIP_INVALID_ITERATOR, false);
124 }
125
126
127
128
129
130 SimpleNode sn = (SimpleNode) node.jjtGetChild(0);
131
132 if (sn instanceof ASTReference)
133 {
134 elementKey = ((ASTReference) sn).getRootString();
135 }
136 else
137 {
138
139
140
141
142 elementKey = sn.getFirstToken().image.substring(1);
143 }
144
145
146
147
148
149 uberInfo = new Info(this.getTemplateName(),
150 getLine(),getColumn());
151 }
152
153
154
155
156
157
158
159 protected void put(InternalContextAdapter context, String key, Object value)
160 {
161 context.put(key, value);
162 }
163
164
165
166
167 protected Iterator getIterator(InternalContextAdapter context, Node node)
168 {
169 Iterator i = null;
170
171
172
173 Object iterable = node.value(context);
174 if (iterable != null)
175 {
176 try
177 {
178 i = rsvc.getUberspect().getIterator(iterable, uberInfo);
179 }
180
181
182
183 catch (RuntimeException e)
184 {
185 throw e;
186 }
187 catch (Exception ee)
188 {
189 String msg = "Error getting iterator for #foreach parameter "
190 + node.literal() + " at " + Log.formatFileString(node);
191 rsvc.getLog().error(msg, ee);
192 throw new VelocityException(msg, ee);
193 }
194
195 if (i == null && !skipInvalidIterator)
196 {
197 String msg = "#foreach parameter " + node.literal() + " at "
198 + Log.formatFileString(node) + " is of type " + iterable.getClass().getName()
199 + " and cannot be iterated by " + rsvc.getUberspect().getClass().getName();
200 rsvc.getLog().error(msg);
201 throw new VelocityException(msg);
202 }
203 }
204 return i;
205 }
206
207
208
209
210
211
212
213
214
215 public boolean render(InternalContextAdapter context, Writer writer, Node node)
216 throws IOException
217 {
218 Iterator i = getIterator(context, node.jjtGetChild(2));
219 if (i == null)
220 {
221 return false;
222 }
223
224
225 Node block = node.jjtGetChild(node.jjtGetNumChildren()-1);
226
227
228
229
230 Object o = context.get(elementKey);
231
232
233
234
235 ForeachScope foreach = null;
236 if (isScopeProvided())
237 {
238 String name = getScopeName();
239 foreach = new ForeachScope(this, context.get(name));
240 context.put(name, foreach);
241 }
242
243 int count = 1;
244 while (count <= maxNbrLoops && i.hasNext())
245 {
246 count++;
247
248 put(context, elementKey, i.next());
249 if (isScopeProvided())
250 {
251
252 foreach.index++;
253 foreach.hasNext = i.hasNext();
254 }
255
256 try
257 {
258 renderBlock(context, writer, block);
259 }
260 catch (StopCommand stop)
261 {
262 if (stop.isFor(this))
263 {
264 break;
265 }
266 else
267 {
268
269 clean(context, o);
270 throw stop;
271 }
272 }
273 }
274 clean(context, o);
275 return true;
276 }
277
278 protected void renderBlock(InternalContextAdapter context, Writer writer, Node block)
279 throws IOException
280 {
281 block.render(context, writer);
282 }
283
284 protected void clean(InternalContextAdapter context, Object o)
285 {
286
287
288
289
290 if (o != null)
291 {
292 context.put(elementKey, o);
293 }
294 else
295 {
296 context.remove(elementKey);
297 }
298
299
300 postRender(context);
301 }
302
303
304
305
306
307 public void checkArgs(ArrayList<Integer> argtypes, Token t, String templateName)
308 throws ParseException
309 {
310 if (argtypes.size() < 3)
311 {
312 throw new MacroParseException("Too few arguments to the #foreach directive",
313 templateName, t);
314 }
315 else if (argtypes.get(0) != ParserTreeConstants.JJTREFERENCE)
316 {
317 throw new MacroParseException("Expected argument 1 of #foreach to be a reference",
318 templateName, t);
319 }
320 else if (argtypes.get(1) != ParserTreeConstants.JJTWORD)
321 {
322 throw new MacroParseException("Expected word 'in' at argument position 2 in #foreach",
323 templateName, t);
324 }
325 else if (argtypes.get(2) == ParserTreeConstants.JJTWORD)
326 {
327 throw new MacroParseException("Argument 3 of #foreach is of the wrong type",
328 templateName, t);
329 }
330 }
331 }