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.Iterator;
25 import java.util.List;
26
27 import org.apache.velocity.app.event.EventCartridge;
28 import org.apache.velocity.context.ChainedInternalContextAdapter;
29 import org.apache.velocity.context.Context;
30 import org.apache.velocity.context.InternalContextAdapter;
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.exception.VelocityException;
36 import org.apache.velocity.runtime.RuntimeConstants;
37 import org.apache.velocity.runtime.RuntimeServices;
38 import org.apache.velocity.runtime.parser.node.ASTReference;
39 import org.apache.velocity.runtime.parser.node.Node;
40 import org.apache.velocity.runtime.parser.node.SimpleNode;
41 import org.apache.velocity.runtime.resource.Resource;
42 import org.apache.velocity.util.introspection.Info;
43 import org.apache.velocity.util.introspection.IntrospectionCacheData;
44
45
46
47
48
49
50
51
52
53
54 public class Foreach extends Directive
55 {
56
57
58
59
60
61
62 protected static class NullHolderContext extends ChainedInternalContextAdapter
63 {
64 private String loopVariableKey = "";
65 private boolean active = true;
66
67
68
69
70
71
72 private NullHolderContext( String key, InternalContextAdapter context )
73 {
74 super(context);
75 if( key != null )
76 loopVariableKey = key;
77 }
78
79
80
81
82
83
84 public Object get( String key ) throws MethodInvocationException
85 {
86 return ( active && loopVariableKey.equals(key) )
87 ? null
88 : super.get(key);
89 }
90
91
92
93
94 public Object put( String key, Object value )
95 {
96 if( loopVariableKey.equals(key) && (value == null) )
97 {
98 active = true;
99 }
100
101 return super.put( key, value );
102 }
103
104
105
106
107
108
109
110
111
112
113 public Object localPut(final String key, final Object value)
114 {
115 return put(key, value);
116 }
117
118
119
120
121
122 public Object remove(Object key)
123 {
124 if( loopVariableKey.equals(key) )
125 {
126 active = false;
127 }
128 return super.remove(key);
129 }
130 }
131
132
133
134
135
136 public String getName()
137 {
138 return "foreach";
139 }
140
141
142
143
144
145 public int getType()
146 {
147 return BLOCK;
148 }
149
150
151
152
153
154
155 private String counterName;
156
157
158
159
160
161
162 private String hasNextName;
163
164
165
166
167 private int counterInitialValue;
168
169
170
171
172 private int maxNbrLoops;
173
174
175
176
177 private boolean skipInvalidIterator;
178
179
180
181
182
183
184
185
186
187
188
189 private String elementKey;
190
191
192
193
194 protected Info uberInfo;
195
196
197
198
199
200
201
202
203
204 public void init(RuntimeServices rs, InternalContextAdapter context, Node node)
205 throws TemplateInitException
206 {
207 super.init(rs, context, node);
208
209 counterName = rsvc.getString(RuntimeConstants.COUNTER_NAME);
210 hasNextName = rsvc.getString(RuntimeConstants.HAS_NEXT_NAME);
211 counterInitialValue = rsvc.getInt(RuntimeConstants.COUNTER_INITIAL_VALUE);
212 maxNbrLoops = rsvc.getInt(RuntimeConstants.MAX_NUMBER_LOOPS,
213 Integer.MAX_VALUE);
214 if (maxNbrLoops < 1)
215 {
216 maxNbrLoops = Integer.MAX_VALUE;
217 }
218 skipInvalidIterator =
219 rsvc.getBoolean(RuntimeConstants.SKIP_INVALID_ITERATOR, true);
220
221
222
223
224
225
226 SimpleNode sn = (SimpleNode) node.jjtGetChild(0);
227
228 if (sn instanceof ASTReference)
229 {
230 elementKey = ((ASTReference) sn).getRootString();
231 }
232 else
233 {
234
235
236
237
238 elementKey = sn.getFirstToken().image.substring(1);
239 }
240
241
242
243
244
245 uberInfo = new Info(context.getCurrentTemplateName(),
246 getLine(),getColumn());
247 }
248
249
250
251
252
253
254
255 protected void put(InternalContextAdapter context, String key, Object value)
256 {
257 context.put(key, value);
258 }
259
260
261
262
263
264
265
266
267
268
269
270
271 public boolean render(InternalContextAdapter context,
272 Writer writer, Node node)
273 throws IOException, MethodInvocationException, ResourceNotFoundException,
274 ParseErrorException
275 {
276
277
278
279
280 Object listObject = node.jjtGetChild(2).value(context);
281
282 if (listObject == null)
283 return false;
284
285 Iterator i = null;
286
287 try
288 {
289 i = rsvc.getUberspect().getIterator(listObject, uberInfo);
290 }
291
292
293
294 catch( RuntimeException e )
295 {
296 throw e;
297 }
298 catch(Exception ee)
299 {
300 String msg = "Error getting iterator for #foreach at "+uberInfo;
301 rsvc.getLog().error(msg, ee);
302 throw new VelocityException(msg, ee);
303 }
304
305 if (i == null)
306 {
307 if (skipInvalidIterator)
308 {
309 return false;
310 }
311 else
312 {
313 Node pnode = node.jjtGetChild(2);
314 String msg = "#foreach parameter " + pnode.literal() + " at "
315 + rsvc.getLog().formatFileString(uberInfo.getTemplateName(),
316 pnode.getLine(), pnode.getColumn())
317 + " is of type " + listObject.getClass().getName()
318 + " and is either of wrong type or cannot be iterated.";
319 rsvc.getLog().error(msg);
320 throw new VelocityException(msg);
321 }
322 }
323
324 int counter = counterInitialValue;
325 boolean maxNbrLoopsExceeded = false;
326
327
328
329
330 Object o = context.get(elementKey);
331 Object savedCounter = context.get(counterName);
332
333
334
335
336
337
338 NullHolderContext nullHolderContext = null;
339
340 while (!maxNbrLoopsExceeded && i.hasNext())
341 {
342
343 put(context, counterName , new Integer(counter));
344 put(context, hasNextName, Boolean.valueOf(i.hasNext()));
345 Object value = i.next();
346 put(context, elementKey, value);
347
348 try
349 {
350
351
352
353 if (value == null)
354 {
355 if (nullHolderContext == null)
356 {
357
358 nullHolderContext = new NullHolderContext(elementKey, context);
359 }
360 node.jjtGetChild(3).render(nullHolderContext, writer);
361 }
362 else
363 {
364 node.jjtGetChild(3).render(context, writer);
365 }
366 }
367 catch (Break.BreakException ex)
368 {
369
370 break;
371 }
372
373 counter++;
374
375
376
377 maxNbrLoopsExceeded = (counter - counterInitialValue) >= maxNbrLoops;
378 }
379
380
381
382
383
384
385 if (savedCounter != null)
386 {
387 context.put(counterName, savedCounter);
388 }
389 else
390 {
391 context.remove(counterName);
392 }
393
394
395
396
397
398
399
400 if (o != null)
401 {
402 context.put(elementKey, o);
403 }
404 else
405 {
406 context.remove(elementKey);
407 }
408
409 return true;
410 }
411 }