1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.view.facelets.tag.jstl.core;
20
21 import java.io.IOException;
22 import java.io.Serializable;
23 import java.lang.reflect.Array;
24 import java.util.Collection;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28
29 import javax.el.ELException;
30 import javax.el.ValueExpression;
31 import javax.faces.FacesException;
32 import javax.faces.application.StateManager;
33 import javax.faces.component.UIComponent;
34 import javax.faces.event.PhaseId;
35 import javax.faces.view.facelets.FaceletContext;
36 import javax.faces.view.facelets.FaceletException;
37 import javax.faces.view.facelets.TagAttribute;
38 import javax.faces.view.facelets.TagAttributeException;
39 import javax.faces.view.facelets.TagConfig;
40 import javax.faces.view.facelets.TagHandler;
41
42 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFFaceletAttribute;
43 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFFaceletTag;
44 import org.apache.myfaces.view.facelets.AbstractFaceletContext;
45 import org.apache.myfaces.view.facelets.FaceletCompositionContext;
46 import org.apache.myfaces.view.facelets.PageContext;
47 import org.apache.myfaces.view.facelets.el.FaceletStateValueExpression;
48 import org.apache.myfaces.view.facelets.tag.ComponentContainerHandler;
49 import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport;
50 import org.apache.myfaces.view.facelets.tag.jsf.FaceletState;
51
52
53
54
55
56
57
58
59
60
61 @JSFFaceletTag(name="c:forEach")
62 public final class ForEachHandler extends TagHandler implements ComponentContainerHandler
63 {
64
65 private static class ArrayIterator implements Iterator<Object>
66 {
67
68 protected final Object array;
69
70 protected int i;
71
72 protected final int len;
73
74 public ArrayIterator(Object src)
75 {
76 this.i = 0;
77 this.array = src;
78 this.len = Array.getLength(src);
79 }
80
81 @Override
82 public boolean hasNext()
83 {
84 return this.i < this.len;
85 }
86
87 @Override
88 public Object next()
89 {
90 return Array.get(this.array, this.i++);
91 }
92
93 @Override
94 public void remove()
95 {
96 throw new UnsupportedOperationException();
97 }
98 }
99
100
101
102
103
104
105
106
107
108
109 @JSFFaceletAttribute(className="int")
110 private final TagAttribute begin;
111
112
113
114
115
116
117
118
119
120 @JSFFaceletAttribute(className="int")
121 private final TagAttribute end;
122
123
124
125
126 @JSFFaceletAttribute(className="javax.el.ValueExpression")
127 private final TagAttribute items;
128
129
130
131
132
133 @JSFFaceletAttribute(className="int")
134 private final TagAttribute step;
135
136 private final TagAttribute tranzient;
137
138
139
140
141
142
143
144 @JSFFaceletAttribute(className="java.lang.String")
145 private final TagAttribute var;
146
147
148
149
150
151 @JSFFaceletAttribute(className="java.lang.String")
152 private final TagAttribute varStatus;
153
154
155
156
157 public ForEachHandler(TagConfig config)
158 {
159 super(config);
160 this.items = this.getAttribute("items");
161 this.var = this.getAttribute("var");
162 this.begin = this.getAttribute("begin");
163 this.end = this.getAttribute("end");
164 this.step = this.getAttribute("step");
165 this.varStatus = this.getAttribute("varStatus");
166 this.tranzient = this.getAttribute("transient");
167
168 if (this.items == null && this.begin != null && this.end == null)
169 {
170 throw new TagAttributeException(this.tag, this.begin,
171 "If the 'items' attribute is not specified, but the 'begin' attribute is, "
172 + "then the 'end' attribute is required");
173 }
174 }
175
176 @Override
177 public void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, FaceletException,
178 ELException
179 {
180 int e = this.getEnd(ctx);
181 Object src = null;
182 ValueExpression srcVE = null;
183 if (this.items != null)
184 {
185 srcVE = this.items.getValueExpression(ctx, Object.class);
186 src = srcVE.getValue(ctx);
187 }
188 else
189 {
190 byte[] b = new byte[e + 1];
191 for (int i = 0; i < b.length; i++)
192 {
193 b[i] = (byte) i;
194 }
195 src = b;
196 }
197 FaceletCompositionContext fcc = FaceletCompositionContext.getCurrentInstance(ctx);
198 AbstractFaceletContext actx = (AbstractFaceletContext) ctx;
199
200
201
202
203
204 fcc.incrementUniqueComponentId();
205 String uniqueId = actx.generateUniqueFaceletTagId(fcc.generateUniqueId(), tagId);
206 if (src != null)
207 {
208 PageContext pctx = actx.getPageContext();
209
210 FaceletState restoredFaceletState = ComponentSupport.getFaceletState(ctx, parent, false);
211 IterationState restoredSavedOption = (restoredFaceletState == null) ? null :
212 (IterationState) restoredFaceletState.getState(uniqueId);
213
214 if (restoredSavedOption != null)
215 {
216 if (!PhaseId.RESTORE_VIEW.equals(ctx.getFacesContext().getCurrentPhaseId()))
217 {
218
219 applyOnRefresh(ctx, fcc, pctx, parent, uniqueId, src, srcVE, restoredSavedOption);
220 }
221 else
222 {
223
224 applyOnRestore(ctx, fcc, pctx, parent, uniqueId, src, srcVE, restoredSavedOption);
225 }
226 }
227 else
228 {
229
230 applyFirstTime(ctx, fcc, pctx, parent, uniqueId, src, srcVE);
231 }
232 }
233
234 if (fcc.isUsingPSSOnThisView() && fcc.isRefreshTransientBuildOnPSS() && !fcc.isRefreshingTransientBuild())
235 {
236
237 ComponentSupport.markComponentToRestoreFully(ctx.getFacesContext(), parent);
238 }
239 if (fcc.isDynamicComponentSection())
240 {
241 ComponentSupport.markComponentToRefreshDynamically(ctx.getFacesContext(), parent);
242 }
243 }
244
245 private void setVar(FaceletContext ctx, UIComponent parent,
246 String uniqueId, String base, boolean t, Object src,
247 ValueExpression srcVE, Object value, String v, int i)
248 {
249
250 if (v != null)
251 {
252 ValueExpression ve;
253 if (t || srcVE == null)
254 {
255 if (value == null)
256 {
257 ve = null;
258 }
259 else
260 {
261 ve = ctx.getExpressionFactory().createValueExpression(
262 value, Object.class);
263 }
264 }
265 else
266 {
267 ve = this.getVarExpr(srcVE, src, value, i);
268 }
269 setVar(ctx, parent, uniqueId, base, v, ve, srcVE);
270 }
271 }
272
273 private void setVar(FaceletContext ctx, UIComponent parent,
274 String uniqueId, String base, String v, ValueExpression ve, ValueExpression srcVE)
275 {
276 AbstractFaceletContext actx = ((AbstractFaceletContext) ctx);
277 PageContext pctx = actx.getPageContext();
278
279
280 if (srcVE != null)
281 {
282 FaceletState faceletState = ComponentSupport.getFaceletState(ctx, parent, true);
283 faceletState.putBinding(uniqueId, base, ve);
284
285
286 ValueExpression fve = new FaceletStateValueExpression(uniqueId, base);
287 pctx.getAttributes().put(v, fve);
288 }
289 else
290 {
291 pctx.getAttributes().put(v, ve);
292 }
293 }
294
295 private void applyFirstTime(FaceletContext ctx, FaceletCompositionContext fcc, PageContext pctx,
296 UIComponent parent, String uniqueId, Object src, ValueExpression srcVE) throws IOException
297 {
298 int s = this.getBegin(ctx);
299 int e = this.getEnd(ctx);
300 int m = this.getStep(ctx);
301 Integer sO = this.begin != null ? Integer.valueOf(s) : null;
302 Integer eO = this.end != null ? Integer.valueOf(e) : null;
303 Integer mO = this.step != null ? Integer.valueOf(m) : null;
304 boolean t = this.getTransient(ctx);
305 IterationState iterationState = new IterationState();
306
307 boolean serializableValues = true;
308 Iterator<?> itr = this.toIterator(src);
309 if (itr != null)
310 {
311 int i = 0;
312
313
314 while (i < s && itr.hasNext())
315 {
316 itr.next();
317 i++;
318 }
319
320 String v = this.getVarName(ctx);
321 String vs = this.getVarStatusName(ctx);
322 ValueExpression vO = this.capture(v, pctx);
323 ValueExpression vsO = this.capture(vs, pctx);
324 int mi = 0;
325 Object value = null;
326 try
327 {
328 boolean first = true;
329 while (i <= e && itr.hasNext())
330 {
331 value = itr.next();
332
333
334 Integer count = iterationState.getCounter();
335 String base = count.toString();
336 iterationState.setCounter(count+1);
337
338 if (value instanceof Serializable)
339 {
340 iterationState.getValueList().add(
341 new Object[]{base, value, i});
342 }
343 else
344 {
345 serializableValues = false;
346 }
347
348 try
349 {
350 fcc.startComponentUniqueIdSection(base);
351
352 setVar(ctx, parent, uniqueId, base, t, src, srcVE, value, v, i);
353 boolean last = !itr.hasNext();
354
355 if (vs != null)
356 {
357 IterationStatus itrS = new IterationStatus(first, last, i, sO, eO, mO, value);
358 ValueExpression ve;
359 if (t || srcVE == null)
360 {
361 if (srcVE == null)
362 {
363 ve = null;
364 }
365 else
366 {
367 ve = ctx.getExpressionFactory().createValueExpression(
368 itrS, Object.class);
369 }
370 }
371 else
372 {
373 ve = new IterationStatusExpression(itrS);
374 }
375 setVar(ctx, parent, uniqueId, base+"_vs", vs, ve, srcVE);
376 }
377
378
379 this.nextHandler.apply(ctx, parent);
380 }
381 finally
382 {
383 fcc.endComponentUniqueIdSection(base);
384 }
385
386
387 mi = 1;
388 while (mi < m && itr.hasNext())
389 {
390 itr.next();
391 mi++;
392 i++;
393 }
394 i++;
395
396 first = false;
397 }
398 }
399 finally
400 {
401 removeVarAndVarStatus(pctx, v, vO, vs, vsO);
402 }
403 }
404 if (serializableValues)
405 {
406 FaceletState faceletState = ComponentSupport.getFaceletState(ctx, parent, true);
407 faceletState.putState(uniqueId, iterationState);
408 }
409 }
410
411 private void applyOnRestore(FaceletContext ctx, FaceletCompositionContext fcc, PageContext pctx,
412 UIComponent parent, String uniqueId, Object src, ValueExpression srcVE, IterationState restoredSavedOption)
413 throws IOException
414 {
415 int s = this.getBegin(ctx);
416 int e = this.getEnd(ctx);
417 int m = this.getStep(ctx);
418 Integer sO = this.begin != null ? Integer.valueOf(s) : null;
419 Integer eO = this.end != null ? Integer.valueOf(e) : null;
420 Integer mO = this.step != null ? Integer.valueOf(m) : null;
421 boolean t = this.getTransient(ctx);
422
423
424 String v = this.getVarName(ctx);
425 String vs = this.getVarStatusName(ctx);
426 ValueExpression vO = this.capture(v, pctx);
427 ValueExpression vsO = this.capture(vs, pctx);
428 Object value = null;
429 try
430 {
431 int size = restoredSavedOption.getValueList().size();
432 for (int si = 0; si < size; si++)
433 {
434 Object[] stateValue = restoredSavedOption.getValueList().get(si);
435 value = stateValue[1];
436 String base = (String) stateValue[0];
437
438 try
439 {
440 fcc.startComponentUniqueIdSection(base);
441
442 setVar(ctx, parent, uniqueId, base, t, src, srcVE, value, v, (Integer) stateValue[2]);
443
444 boolean first = (si == 0);
445 boolean last = (si == size-1);
446 int i = (Integer)stateValue[2];
447
448 if (vs != null)
449 {
450 IterationStatus itrS = new IterationStatus(first, last, i, sO, eO, mO, value);
451 ValueExpression ve;
452 if (t || srcVE == null)
453 {
454 if (srcVE == null)
455 {
456 ve = null;
457 }
458 else
459 {
460 ve = ctx.getExpressionFactory().createValueExpression(
461 itrS, Object.class);
462 }
463 }
464 else
465 {
466 ve = new IterationStatusExpression(itrS);
467 }
468 setVar(ctx, parent, uniqueId, base+"_vs", vs, ve, srcVE);
469 }
470
471
472 this.nextHandler.apply(ctx, parent);
473 }
474 finally
475 {
476 fcc.endComponentUniqueIdSection(base);
477 }
478 }
479 }
480 finally
481 {
482 removeVarAndVarStatus(pctx, v, vO, vs, vsO);
483 }
484 }
485
486 private void applyOnRefresh(FaceletContext ctx, FaceletCompositionContext fcc, PageContext pctx,
487 UIComponent parent, String uniqueId, Object src, ValueExpression srcVE, IterationState restoredSavedOption)
488 throws IOException
489 {
490 int s = this.getBegin(ctx);
491 int e = this.getEnd(ctx);
492 int m = this.getStep(ctx);
493 Integer sO = this.begin != null ? Integer.valueOf(s) : null;
494 Integer eO = this.end != null ? Integer.valueOf(e) : null;
495 Integer mO = this.step != null ? Integer.valueOf(m) : null;
496 boolean t = this.getTransient(ctx);
497
498
499 Iterator<?> itr = this.toIterator(src);
500 IterationState iterationState = new IterationState();
501 iterationState.setCounter(restoredSavedOption.getCounter());
502 if (itr != null)
503 {
504 int i = 0;
505
506
507 while (i < s && itr.hasNext())
508 {
509 itr.next();
510 i++;
511 }
512
513 String v = this.getVarName(ctx);
514 String vs = this.getVarStatusName(ctx);
515 ValueExpression vO = this.capture(v, pctx);
516 ValueExpression vsO = this.capture(vs, pctx);
517 int mi = 0;
518 Object value = null;
519 int stateIndex = 0;
520 try
521 {
522 boolean first = true;
523 while (i <= e && itr.hasNext())
524 {
525 value = itr.next();
526 Object[] stateValue = null;
527 String base = null;
528 boolean found = false;
529
530
531
532
533 int stateIndexCheck = stateIndex;
534 for (; stateIndexCheck < restoredSavedOption.getValueList().size(); stateIndexCheck++)
535 {
536 stateValue = restoredSavedOption.getValueList().get(stateIndexCheck);
537 if (value.equals(stateValue[1]))
538 {
539 found = true;
540 break;
541 }
542 }
543 if (found)
544 {
545 stateIndex = stateIndexCheck;
546 base = (String) stateValue[0];
547 stateIndex++;
548 }
549 else
550 {
551
552 Integer count = iterationState.getCounter();
553 base = count.toString();
554 iterationState.setCounter(count+1);
555 stateValue = null;
556 }
557
558 if (value instanceof Serializable)
559 {
560 iterationState.getValueList().add(
561 new Object[]{base, value, i});
562 }
563
564 try
565 {
566 fcc.startComponentUniqueIdSection(base);
567
568 setVar(ctx, parent, uniqueId, base, t, src, srcVE, value, v, i);
569
570 boolean last = !itr.hasNext();
571
572 if (vs != null)
573 {
574 IterationStatus itrS = new IterationStatus(first, last, i, sO, eO, mO, value);
575 ValueExpression ve;
576 if (t || srcVE == null)
577 {
578 if (srcVE == null)
579 {
580 ve = null;
581 }
582 else
583 {
584 ve = ctx.getExpressionFactory().createValueExpression(
585 itrS, Object.class);
586 }
587 }
588 else
589 {
590 ve = new IterationStatusExpression(itrS);
591 }
592 setVar(ctx, parent, uniqueId, base+"_vs", vs, ve, srcVE);
593 }
594
595
596
597 boolean markInitialState = (stateValue == null);
598 boolean oldMarkInitialState = false;
599 Boolean isBuildingInitialState = null;
600 try
601 {
602 if (markInitialState && fcc.isUsingPSSOnThisView())
603 {
604
605 oldMarkInitialState = fcc.isMarkInitialState();
606 fcc.setMarkInitialState(true);
607 isBuildingInitialState = (Boolean) ctx.getFacesContext().getAttributes().put(
608 StateManager.IS_BUILDING_INITIAL_STATE, Boolean.TRUE);
609 }
610 this.nextHandler.apply(ctx, parent);
611 }
612 finally
613 {
614 if (markInitialState && fcc.isUsingPSSOnThisView())
615 {
616
617 if (isBuildingInitialState == null)
618 {
619 ctx.getFacesContext().getAttributes().remove(
620 StateManager.IS_BUILDING_INITIAL_STATE);
621 }
622 else
623 {
624 ctx.getFacesContext().getAttributes().put(
625 StateManager.IS_BUILDING_INITIAL_STATE, isBuildingInitialState);
626 }
627 fcc.setMarkInitialState(oldMarkInitialState);
628 }
629 }
630 }
631 finally
632 {
633 fcc.endComponentUniqueIdSection(base);
634 }
635
636
637 mi = 1;
638 while (mi < m && itr.hasNext())
639 {
640 itr.next();
641 mi++;
642 i++;
643 }
644 i++;
645
646 first = false;
647 }
648 }
649 finally
650 {
651 removeVarAndVarStatus(pctx, v, vO, vs, vsO);
652 }
653 }
654 FaceletState faceletState = ComponentSupport.getFaceletState(ctx, parent, true);
655 faceletState.putState(uniqueId, iterationState);
656 }
657
658 private void removeVarAndVarStatus(PageContext pctx, String v, ValueExpression vO, String vs, ValueExpression vsO)
659 {
660
661 if (v != null)
662 {
663 pctx.getAttributes().put(v, vO);
664 }
665 else
666 {
667 pctx.getAttributes().remove(v);
668 }
669 if (vs != null)
670 {
671 pctx.getAttributes().put(vs, vsO);
672 }
673 else
674 {
675 pctx.getAttributes().remove(vs);
676 }
677 }
678
679 private ValueExpression capture(String name, PageContext pctx)
680 {
681 if (name != null)
682 {
683 return pctx.getAttributes().put(name, null);
684 }
685 return null;
686 }
687
688 private int getBegin(FaceletContext ctx)
689 {
690 if (this.begin != null)
691 {
692 return this.begin.getInt(ctx);
693 }
694 return 0;
695 }
696
697 private int getEnd(FaceletContext ctx)
698 {
699 if (this.end != null)
700 {
701 return this.end.getInt(ctx);
702 }
703 return Integer.MAX_VALUE - 1;
704 }
705
706 private int getStep(FaceletContext ctx)
707 {
708 if (this.step != null)
709 {
710 return this.step.getInt(ctx);
711 }
712 return 1;
713 }
714
715 private boolean getTransient(FaceletContext ctx)
716 {
717 if (this.tranzient != null)
718 {
719 return this.tranzient.getBoolean(ctx);
720 }
721 return false;
722 }
723
724 private ValueExpression getVarExpr(ValueExpression ve, Object src, Object value, int i)
725 {
726 if (src instanceof List || src.getClass().isArray())
727 {
728
729 return new IteratedValueExpression(ve, value);
730 }
731 else if (src instanceof Map && value instanceof Map.Entry)
732 {
733 return new MappedValueExpression(ve, (Map.Entry) value);
734 }
735 else if (src instanceof Collection)
736 {
737 return new IteratedValueExpression(ve, value);
738 }
739 throw new IllegalStateException("Cannot create VE for: " + src);
740 }
741
742 private String getVarName(FaceletContext ctx)
743 {
744 if (this.var != null)
745 {
746 return this.var.getValue(ctx);
747 }
748 return null;
749 }
750
751 private String getVarStatusName(FaceletContext ctx)
752 {
753 if (this.varStatus != null)
754 {
755 return this.varStatus.getValue(ctx);
756 }
757 return null;
758 }
759
760 private Iterator<?> toIterator(Object src)
761 {
762 if (src == null)
763 {
764 return null;
765 }
766 else if (src instanceof Collection)
767 {
768 return ((Collection<?>) src).iterator();
769 }
770 else if (src instanceof Map)
771 {
772 return ((Map<?, ?>) src).entrySet().iterator();
773 }
774 else if (src.getClass().isArray())
775 {
776 return new ArrayIterator(src);
777 }
778 else
779 {
780 throw new TagAttributeException(this.tag, this.items,
781 "Must evaluate to a Collection, Map, Array, or null.");
782 }
783 }
784
785 }