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
26 import org.apache.velocity.app.event.EventCartridge;
27 import org.apache.velocity.context.Context;
28 import org.apache.velocity.context.InternalContextAdapter;
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.RuntimeConstants;
34 import org.apache.velocity.runtime.RuntimeServices;
35 import org.apache.velocity.runtime.parser.node.ASTReference;
36 import org.apache.velocity.runtime.parser.node.Node;
37 import org.apache.velocity.runtime.parser.node.SimpleNode;
38 import org.apache.velocity.runtime.resource.Resource;
39 import org.apache.velocity.util.introspection.Info;
40 import org.apache.velocity.util.introspection.IntrospectionCacheData;
41
42
43
44
45
46
47
48
49
50
51 public class Foreach extends Directive
52 {
53
54
55
56
57
58 protected static class NullHolderContext implements InternalContextAdapter
59 {
60 private InternalContextAdapter innerContext = null;
61 private String loopVariableKey = "";
62 private boolean active = true;
63
64
65
66
67
68
69 private NullHolderContext( String key, InternalContextAdapter context )
70 {
71 innerContext = context;
72 if( key != null )
73 loopVariableKey = key;
74 }
75
76
77
78
79
80
81 public Object get( String key ) throws MethodInvocationException
82 {
83 return ( active && loopVariableKey.equals(key) )
84 ? null
85 : innerContext.get(key);
86 }
87
88
89
90
91 public Object put( String key, Object value )
92 {
93 if( loopVariableKey.equals(key) && (value == null) )
94 {
95 active = true;
96 }
97
98 return innerContext.put( key, value );
99 }
100
101
102
103
104
105
106
107
108
109
110 public Object localPut(final String key, final Object value)
111 {
112 return put(key, value);
113 }
114
115
116
117
118
119 public boolean containsKey( Object key )
120 {
121 return innerContext.containsKey(key);
122 }
123
124
125
126
127 public Object[] getKeys()
128 {
129 return innerContext.getKeys();
130 }
131
132
133
134
135
136 public Object remove(Object key)
137 {
138 if( loopVariableKey.equals(key) )
139 {
140 active = false;
141 }
142 return innerContext.remove(key);
143 }
144
145
146
147
148 public void pushCurrentTemplateName(String s)
149 {
150 innerContext.pushCurrentTemplateName(s);
151 }
152
153
154
155
156 public void popCurrentTemplateName()
157 {
158 innerContext.popCurrentTemplateName();
159 }
160
161
162
163
164 public String getCurrentTemplateName()
165 {
166 return innerContext.getCurrentTemplateName();
167 }
168
169
170
171
172 public Object[] getTemplateNameStack()
173 {
174 return innerContext.getTemplateNameStack();
175 }
176
177
178
179
180 public IntrospectionCacheData icacheGet(Object key)
181 {
182 return innerContext.icacheGet(key);
183 }
184
185
186
187
188 public void icachePut(Object key, IntrospectionCacheData o)
189 {
190 innerContext.icachePut(key,o);
191 }
192
193
194
195
196 public void setCurrentResource( Resource r )
197 {
198 innerContext.setCurrentResource(r);
199 }
200
201
202
203
204 public Resource getCurrentResource()
205 {
206 return innerContext.getCurrentResource();
207 }
208
209
210
211
212 public InternalContextAdapter getBaseContext()
213 {
214 return innerContext.getBaseContext();
215 }
216
217
218
219
220 public Context getInternalUserContext()
221 {
222 return innerContext.getInternalUserContext();
223 }
224
225
226
227
228 public EventCartridge attachEventCartridge(EventCartridge ec)
229 {
230 EventCartridge cartridge = innerContext.attachEventCartridge( ec );
231
232 return cartridge;
233 }
234
235
236
237
238 public EventCartridge getEventCartridge()
239 {
240 return innerContext.getEventCartridge();
241 }
242
243
244
245
246 public boolean getAllowRendering()
247 {
248 return innerContext.getAllowRendering();
249 }
250
251
252
253
254 public void setAllowRendering(boolean v)
255 {
256 innerContext.setAllowRendering(v);
257 }
258
259 }
260
261
262
263
264
265 public String getName()
266 {
267 return "foreach";
268 }
269
270
271
272
273
274 public int getType()
275 {
276 return BLOCK;
277 }
278
279
280
281
282
283
284 private String counterName;
285
286
287
288
289 private int counterInitialValue;
290
291
292
293
294 private int maxNbrLoops;
295
296
297
298
299
300
301
302
303
304
305
306 private String elementKey;
307
308
309
310
311 protected Info uberInfo;
312
313
314
315
316
317
318
319
320
321 public void init(RuntimeServices rs, InternalContextAdapter context, Node node)
322 throws TemplateInitException
323 {
324 super.init(rs, context, node);
325
326 counterName = rsvc.getString(RuntimeConstants.COUNTER_NAME);
327 counterInitialValue = rsvc.getInt(RuntimeConstants.COUNTER_INITIAL_VALUE);
328 maxNbrLoops = rsvc.getInt(RuntimeConstants.MAX_NUMBER_LOOPS,
329 Integer.MAX_VALUE);
330 if (maxNbrLoops < 1)
331 {
332 maxNbrLoops = Integer.MAX_VALUE;
333 }
334
335
336
337
338
339
340 SimpleNode sn = (SimpleNode) node.jjtGetChild(0);
341
342 if (sn instanceof ASTReference)
343 {
344 elementKey = ((ASTReference) sn).getRootString();
345 }
346 else
347 {
348
349
350
351
352 elementKey = sn.getFirstToken().image.substring(1);
353 }
354
355
356
357
358
359 uberInfo = new Info(context.getCurrentTemplateName(),
360 getLine(),getColumn());
361 }
362
363
364
365
366
367
368
369
370
371
372
373
374 public boolean render(InternalContextAdapter context,
375 Writer writer, Node node)
376 throws IOException, MethodInvocationException, ResourceNotFoundException,
377 ParseErrorException
378 {
379
380
381
382
383 Object listObject = node.jjtGetChild(2).value(context);
384
385 if (listObject == null)
386 return false;
387
388 Iterator i = null;
389
390 try
391 {
392 i = rsvc.getUberspect().getIterator(listObject, uberInfo);
393 }
394
395
396
397 catch( RuntimeException e )
398 {
399 throw e;
400 }
401 catch(Exception ee)
402 {
403 rsvc.getLog().error("Error getting iterator for #foreach", ee);
404 }
405
406 if (i == null)
407 {
408 return false;
409 }
410
411 int counter = counterInitialValue;
412 boolean maxNbrLoopsExceeded = false;
413
414
415
416
417 Object o = context.get(elementKey);
418 Object savedCounter = context.get(counterName);
419
420
421
422
423
424
425 NullHolderContext nullHolderContext = null;
426
427 while (!maxNbrLoopsExceeded && i.hasNext())
428 {
429
430 context.localPut(counterName , new Integer(counter));
431 Object value = i.next();
432 context.localPut(elementKey, value);
433
434
435
436
437 if( value == null )
438 {
439 if( nullHolderContext == null )
440 {
441
442 nullHolderContext = new NullHolderContext(elementKey, context);
443 }
444 node.jjtGetChild(3).render(nullHolderContext, writer);
445 }
446 else
447 {
448 node.jjtGetChild(3).render(context, writer);
449 }
450 counter++;
451
452
453
454 maxNbrLoopsExceeded = (counter - counterInitialValue) >= maxNbrLoops;
455 }
456
457
458
459
460
461
462 if (savedCounter != null)
463 {
464 context.put(counterName, savedCounter);
465 }
466 else
467 {
468 context.remove(counterName);
469 }
470
471
472
473
474
475
476
477 if (o != null)
478 {
479 context.put(elementKey, o);
480 }
481 else
482 {
483 context.remove(elementKey);
484 }
485
486 return true;
487 }
488 }