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