View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.view.facelets.component;
20  
21  import java.io.IOException;
22  import java.io.Serializable;
23  import java.sql.ResultSet;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.HashMap;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  
32  import javax.el.ValueExpression;
33  import javax.faces.FacesException;
34  import javax.faces.application.FacesMessage;
35  import javax.faces.component.ContextCallback;
36  import javax.faces.component.EditableValueHolder;
37  import javax.faces.component.NamingContainer;
38  import javax.faces.component.UIComponent;
39  import javax.faces.component.UIComponentBase;
40  import javax.faces.component.UINamingContainer;
41  import javax.faces.component.visit.VisitCallback;
42  import javax.faces.component.visit.VisitContext;
43  import javax.faces.component.visit.VisitHint;
44  import javax.faces.component.visit.VisitResult;
45  import javax.faces.context.FacesContext;
46  import javax.faces.event.AbortProcessingException;
47  import javax.faces.event.FacesEvent;
48  import javax.faces.event.FacesListener;
49  import javax.faces.event.PhaseId;
50  import javax.faces.model.ArrayDataModel;
51  import javax.faces.model.DataModel;
52  import javax.faces.model.ListDataModel;
53  import javax.faces.model.ResultSetDataModel;
54  import javax.faces.model.ScalarDataModel;
55  import javax.faces.render.Renderer;
56  
57  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
58  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
59  
60  /**
61   *  
62   */
63  @JSFComponent(name="ui:repeat", defaultRendererType="facelets.ui.Repeat")
64  public class UIRepeat extends UIComponentBase implements NamingContainer
65  {
66      public static final String COMPONENT_TYPE = "facelets.ui.Repeat";
67  
68      public static final String COMPONENT_FAMILY = "facelets";
69      
70      //private static final String SKIP_ITERATION_HINT = "javax.faces.visit.SKIP_ITERATION";
71  
72      private final static DataModel<?> EMPTY_MODEL = new ListDataModel<Object>(Collections.emptyList());
73      
74      private static final Class<Object[]> OBJECT_ARRAY_CLASS = Object[].class;
75  
76      private static final Object[] LEAF_NO_STATE = new Object[]{null,null};
77      
78      private Object _initialDescendantComponentState = null;
79  
80      // Holds for each row the states of the child components of this UIData.
81      // Note that only "partial" component state is saved: the component fields
82      // that are expected to vary between rows.
83      private Map<String, Collection<Object[]>> _rowStates = new HashMap<String, Collection<Object[]>>();
84      
85      /**
86       * Handle case where this table is nested inside another table. See method getDataModel for more details.
87       * <p>
88       * Key: parentClientId (aka rowId when nested within a parent table) Value: DataModel
89       */
90      private Map<String, DataModel> _dataModelMap = new HashMap<String, DataModel>();
91      
92      // will be set to false if the data should not be refreshed at the beginning of the encode phase
93      private boolean _isValidChilds = true;
94  
95      private int _end = -1;
96      
97      private int _count;
98      
99      private int _index = -1;
100 
101     private transient StringBuilder _clientIdBuffer;
102     private transient Object _origValue;
103     private transient Object _origVarStatus;
104 
105     private transient FacesContext _facesContext;
106     
107     public UIRepeat()
108     {
109         setRendererType("facelets.ui.Repeat");
110     }
111 
112     public String getFamily()
113     {
114         return COMPONENT_FAMILY;
115     }
116     
117     @JSFProperty
118     public int getOffset()
119     {
120         return (Integer) getStateHelper().eval(PropertyKeys.offset, 0);
121     }
122 
123     public void setOffset(int offset)
124     {
125         getStateHelper().put(PropertyKeys.offset, offset );
126     }
127     
128     @JSFProperty
129     public int getSize()
130     {
131         return (Integer) getStateHelper().eval(PropertyKeys.size, -1);
132     }
133 
134     public void setSize(int size)
135     {
136         getStateHelper().put(PropertyKeys.size, size );
137     }
138     
139     @JSFProperty
140     public int getStep()
141     {
142         return (Integer) getStateHelper().eval(PropertyKeys.step, 1);
143     }
144 
145     public void setStep(int step)
146     {
147         getStateHelper().put(PropertyKeys.step, step );
148     }
149     
150     @JSFProperty(literalOnly=true)
151     public String getVar()
152     {
153         return (String) getStateHelper().get(PropertyKeys.var);
154     }
155 
156     public void setVar(String var)
157     {
158         getStateHelper().put(PropertyKeys.var, var );
159     }
160     
161     @JSFProperty(literalOnly=true)
162     public String getVarStatus ()
163     {
164         return (String) getStateHelper().get(PropertyKeys.varStatus);
165     }
166     
167     public void setVarStatus (String varStatus)
168     {
169         getStateHelper().put(PropertyKeys.varStatus, varStatus );
170     }
171     
172     protected DataModel getDataModel()
173     {
174         DataModel dataModel;
175         String clientID = "";
176 
177         UIComponent parent = getParent();
178         if (parent != null)
179         {
180             clientID = parent.getContainerClientId(getFacesContext());
181         }
182         dataModel = _dataModelMap.get(clientID);
183         if (dataModel == null)
184         {
185             dataModel = createDataModel();
186             _dataModelMap.put(clientID, dataModel);
187         }
188         return dataModel;
189     }
190     
191     private DataModel createDataModel()
192     {
193         Object value = getValue();
194 
195         if (value == null)
196         {
197             return EMPTY_MODEL;
198         }
199         else if (value instanceof DataModel)
200         {
201             return (DataModel) value;
202         }
203         else if (value instanceof List)
204         {
205             return new ListDataModel((List<?>) value);
206         }
207         else if (OBJECT_ARRAY_CLASS.isAssignableFrom(value.getClass()))
208         {
209             return new ArrayDataModel((Object[]) value);
210         }
211         else if (value instanceof ResultSet)
212         {
213             return new ResultSetDataModel((ResultSet) value);
214         }
215         else
216         {
217             return new ScalarDataModel(value);
218         }
219     }
220     
221     @Override
222     public void setValueExpression(String name, ValueExpression binding)
223     {
224         if (name == null)
225         {
226             throw new NullPointerException("name");
227         }
228         else if (name.equals("value"))
229         {
230             _dataModelMap.clear();
231         }
232         else if (name.equals("rowIndex"))
233         {
234             throw new IllegalArgumentException("name " + name);
235         }
236         super.setValueExpression(name, binding);
237     }
238     
239     @JSFProperty
240     public Object getValue()
241     {
242         return  getStateHelper().eval(PropertyKeys.value);
243     }
244 
245     public void setValue(Object value)
246     {
247         getStateHelper().put(PropertyKeys.value, value);
248         _dataModelMap.clear();
249         _rowStates.clear();
250         _isValidChilds = true;
251     }
252 
253     @Override
254     public String getContainerClientId(FacesContext context)
255     {
256         //MYFACES-2744 UIData.getClientId() should not append rowIndex, instead use UIData.getContainerClientId()
257         String clientId = super.getContainerClientId(context);
258         
259         int index = getIndex();
260         if (index == -1)
261         {
262             return clientId;
263         }
264 
265         StringBuilder bld = _getBuffer(); //SharedStringBuilder(context);
266         return bld.append(clientId).append(UINamingContainer.getSeparatorChar(context)).append(index).toString();
267     }
268     
269     private RepeatStatus _getRepeatStatus()
270     {
271         return new RepeatStatus(_count == 0, _index + getStep() >= getDataModel().getRowCount(),
272             _count, _index, getOffset(), _end, getStep());
273     }
274 
275     private void _captureScopeValues()
276     {
277         String var = getVar();
278         if (var != null)
279         {
280             _origValue = getFacesContext().getExternalContext().getRequestMap().get(var);
281         }
282         String varStatus = getVarStatus();
283         if (varStatus != null)
284         {
285             _origVarStatus = getFacesContext().getExternalContext().getRequestMap().get(varStatus);
286         }
287     }
288     
289     private StringBuilder _getBuffer()
290     {
291         if (_clientIdBuffer == null)
292         {
293             _clientIdBuffer = new StringBuilder();
294         }
295         
296         _clientIdBuffer.setLength(0);
297         
298         return _clientIdBuffer;
299     }
300 
301     private boolean _isIndexAvailable()
302     {
303         return getDataModel().isRowAvailable();
304     }
305 
306     private void _restoreScopeValues()
307     {
308         String var = getVar();
309         if (var != null)
310         {
311             Map<String, Object> attrs = getFacesContext().getExternalContext().getRequestMap();
312             if (_origValue != null)
313             {
314                 attrs.put(var, _origValue);
315                 _origValue = null;
316             }
317             else
318             {
319                 attrs.remove(var);
320             }
321         }
322         String varStatus = getVarStatus();
323         if (getVarStatus() != null)
324         {
325             Map<String, Object> attrs = getFacesContext().getExternalContext().getRequestMap();
326             if (_origVarStatus != null)
327             {
328                 attrs.put(varStatus, _origVarStatus);
329                 _origVarStatus = null;
330             }
331             else
332             {
333                 attrs.remove(varStatus);
334             }
335         }
336     }
337     
338     /**
339      * Overwrite the state of the child components of this component with data previously saved by method
340      * saveDescendantComponentStates.
341      * <p>
342      * The saved state info only covers those fields that are expected to vary between rows of a table. 
343      * Other fields are not modified.
344      */
345     @SuppressWarnings("unchecked")
346     private void restoreDescendantComponentStates(UIComponent parent, boolean iterateFacets, Object state,
347                                                   boolean restoreChildFacets)
348     {
349         int descendantStateIndex = -1;
350         List<? extends Object[]> stateCollection = null;
351         
352         if (iterateFacets && parent.getFacetCount() > 0)
353         {
354             Iterator<UIComponent> childIterator = parent.getFacets().values().iterator();
355             
356             while (childIterator.hasNext())
357             {
358                 UIComponent component = childIterator.next();
359 
360                 // reset the client id (see spec 3.1.6)
361                 component.setId(component.getId());
362                 if (!component.isTransient())
363                 {
364                     if (descendantStateIndex == -1)
365                     {
366                         stateCollection = ((List<? extends Object[]>) state);
367                         descendantStateIndex = stateCollection.isEmpty() ? -1 : 0;
368                     }
369                     
370                     if (descendantStateIndex != -1 && descendantStateIndex < stateCollection.size())
371                     {
372                         Object[] object = stateCollection.get(descendantStateIndex);
373                         if (object[0] != null && component instanceof EditableValueHolder)
374                         {
375                             ((SavedState) object[0]).restoreState((EditableValueHolder) component);
376                         }
377                         // If there is descendant state to restore, call it recursively, otherwise
378                         // it is safe to skip iteration.
379                         if (object[1] != null)
380                         {
381                             restoreDescendantComponentStates(component, restoreChildFacets, object[1], true);
382                         }
383                         else
384                         {
385                             restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
386                         }
387                     }
388                     else
389                     {
390                         restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
391                     }
392                     descendantStateIndex++;
393                 }
394             }
395         }
396         
397         if (parent.getChildCount() > 0)
398         {
399             for (int i = 0; i < parent.getChildCount(); i++)
400             {
401                 UIComponent component = parent.getChildren().get(i);
402 
403                 // reset the client id (see spec 3.1.6)
404                 component.setId(component.getId());
405                 if (!component.isTransient())
406                 {
407                     if (descendantStateIndex == -1)
408                     {
409                         stateCollection = ((List<? extends Object[]>) state);
410                         descendantStateIndex = stateCollection.isEmpty() ? -1 : 0;
411                     }
412                     
413                     if (descendantStateIndex != -1 && descendantStateIndex < stateCollection.size())
414                     {
415                         Object[] object = stateCollection.get(descendantStateIndex);
416                         if (object[0] != null && component instanceof EditableValueHolder)
417                         {
418                             ((SavedState) object[0]).restoreState((EditableValueHolder) component);
419                         }
420                         // If there is descendant state to restore, call it recursively, otherwise
421                         // it is safe to skip iteration.
422                         if (object[1] != null)
423                         {
424                             restoreDescendantComponentStates(component, restoreChildFacets, object[1], true);
425                         }
426                         else
427                         {
428                             restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
429                         }
430                     }
431                     else
432                     {
433                         restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
434                     }
435                     descendantStateIndex++;
436                 }
437             }
438         }
439     }
440 
441     /**
442      * Just call component.setId(component.getId()) to reset all client ids and 
443      * ensure they will be calculated for the current row, but do not waste time
444      * dealing with row state code.
445      * 
446      * @param parent
447      * @param iterateFacets
448      * @param restoreChildFacets 
449      */
450     private void restoreDescendantComponentWithoutRestoreState(UIComponent parent, boolean iterateFacets,
451                                                                boolean restoreChildFacets)
452     {
453         if (iterateFacets && parent.getFacetCount() > 0)
454         {
455             Iterator<UIComponent> childIterator = parent.getFacets().values().iterator();
456             
457             while (childIterator.hasNext())
458             {
459                 UIComponent component = childIterator.next();
460 
461                 // reset the client id (see spec 3.1.6)
462                 component.setId(component.getId());
463                 if (!component.isTransient())
464                 {
465                     restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
466                 }
467             }
468         }
469         
470         if (parent.getChildCount() > 0)
471         {
472             for (int i = 0; i < parent.getChildCount(); i++)
473             {
474                 UIComponent component = parent.getChildren().get(i);
475 
476                 // reset the client id (see spec 3.1.6)
477                 component.setId(component.getId());
478                 if (!component.isTransient())
479                 {
480                     restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
481                 }
482             }
483         }
484     }
485 
486     /**
487      * Walk the tree of child components of this UIData, saving the parts of their state that can vary between rows.
488      * <p>
489      * This is very similar to the process that occurs for normal components when the view is serialized. Transient
490      * components are skipped (no state is saved for them).
491      * <p>
492      * If there are no children then null is returned. If there are one or more children, and all children are transient
493      * then an empty collection is returned; this will happen whenever a table contains only read-only components.
494      * <p>
495      * Otherwise a collection is returned which contains an object for every non-transient child component; that object
496      * may itself contain a collection of the state of that child's child components.
497      */
498     private Collection<Object[]> saveDescendantComponentStates(UIComponent parent, boolean iterateFacets,
499                                                                boolean saveChildFacets)
500     {
501         Collection<Object[]> childStates = null;
502         // Index to indicate how many components has been passed without state to save.
503         int childEmptyIndex = 0;
504         int totalChildCount = 0;
505                 
506         if (iterateFacets && parent.getFacetCount() > 0)
507         {
508             Iterator<UIComponent> childIterator = parent.getFacets().values().iterator();
509 
510             while (childIterator.hasNext())
511             {
512                 UIComponent child = childIterator.next();
513                 if (!child.isTransient())
514                 {
515                     // Add an entry to the collection, being an array of two
516                     // elements. The first element is the state of the children
517                     // of this component; the second is the state of the current
518                     // child itself.
519 
520                     if (child instanceof EditableValueHolder)
521                     {
522                         if (childStates == null)
523                         {
524                             childStates = new ArrayList<Object[]>(
525                                     parent.getFacetCount()
526                                     + parent.getChildCount()
527                                     - totalChildCount
528                                     + childEmptyIndex);
529                             for (int ci = 0; ci < childEmptyIndex; ci++)
530                             {
531                                 childStates.add(LEAF_NO_STATE);
532                             }
533                         }
534                     
535                         childStates.add(child.getChildCount() > 0 ? 
536                                 new Object[]{new SavedState((EditableValueHolder) child),
537                                     saveDescendantComponentStates(child, saveChildFacets, true)} :
538                                 new Object[]{new SavedState((EditableValueHolder) child),
539                                     null});
540                     }
541                     else if (child.getChildCount() > 0 || (saveChildFacets && child.getFacetCount() > 0))
542                     {
543                         Object descendantSavedState = saveDescendantComponentStates(child, saveChildFacets, true);
544                         
545                         if (descendantSavedState == null)
546                         {
547                             if (childStates == null)
548                             {
549                                 childEmptyIndex++;
550                             }
551                             else
552                             {
553                                 childStates.add(LEAF_NO_STATE);
554                             }
555                         }
556                         else
557                         {
558                             if (childStates == null)
559                             {
560                                 childStates = new ArrayList<Object[]>(
561                                         parent.getFacetCount()
562                                         + parent.getChildCount()
563                                         - totalChildCount
564                                         + childEmptyIndex);
565                                 for (int ci = 0; ci < childEmptyIndex; ci++)
566                                 {
567                                     childStates.add(LEAF_NO_STATE);
568                                 }
569                             }
570                             childStates.add(new Object[]{null, descendantSavedState});
571                         }
572                     }
573                     else
574                     {
575                         if (childStates == null)
576                         {
577                             childEmptyIndex++;
578                         }
579                         else
580                         {
581                             childStates.add(LEAF_NO_STATE);
582                         }
583                     }
584                 }
585                 totalChildCount++;
586             }
587         }
588         
589         if (parent.getChildCount() > 0)
590         {
591             for (int i = 0; i < parent.getChildCount(); i++)
592             {
593                 UIComponent child = parent.getChildren().get(i);
594                 if (!child.isTransient())
595                 {
596                     // Add an entry to the collection, being an array of two
597                     // elements. The first element is the state of the children
598                     // of this component; the second is the state of the current
599                     // child itself.
600 
601                     if (child instanceof EditableValueHolder)
602                     {
603                         if (childStates == null)
604                         {
605                             childStates = new ArrayList<Object[]>(
606                                     parent.getFacetCount()
607                                     + parent.getChildCount()
608                                     - totalChildCount
609                                     + childEmptyIndex);
610                             for (int ci = 0; ci < childEmptyIndex; ci++)
611                             {
612                                 childStates.add(LEAF_NO_STATE);
613                             }
614                         }
615                     
616                         childStates.add(child.getChildCount() > 0 ? 
617                                 new Object[]{new SavedState((EditableValueHolder) child),
618                                     saveDescendantComponentStates(child, saveChildFacets, true)} :
619                                 new Object[]{new SavedState((EditableValueHolder) child),
620                                     null});
621                     }
622                     else if (child.getChildCount() > 0 || (saveChildFacets && child.getFacetCount() > 0))
623                     {
624                         Object descendantSavedState = saveDescendantComponentStates(child, saveChildFacets, true);
625                         
626                         if (descendantSavedState == null)
627                         {
628                             if (childStates == null)
629                             {
630                                 childEmptyIndex++;
631                             }
632                             else
633                             {
634                                 childStates.add(LEAF_NO_STATE);
635                             }
636                         }
637                         else
638                         {
639                             if (childStates == null)
640                             {
641                                 childStates = new ArrayList<Object[]>(
642                                         parent.getFacetCount()
643                                         + parent.getChildCount()
644                                         - totalChildCount
645                                         + childEmptyIndex);
646                                 for (int ci = 0; ci < childEmptyIndex; ci++)
647                                 {
648                                     childStates.add(LEAF_NO_STATE);
649                                 }
650                             }
651                             childStates.add(new Object[]{null, descendantSavedState});
652                         }
653                     }
654                     else
655                     {
656                         if (childStates == null)
657                         {
658                             childEmptyIndex++;
659                         }
660                         else
661                         {
662                             childStates.add(LEAF_NO_STATE);
663                         }
664                     }
665                 }
666                 totalChildCount++;
667             }
668         }
669         
670         return childStates;
671     }
672     
673     /**
674      * Returns the rowCount of the underlying DataModel.
675      * @return
676      */
677     public int getRowCount()
678     {
679         return getDataModel().getRowCount();
680     }
681     
682     /**
683      * Returns the current index.
684      */
685     public int getIndex()
686     {
687         return _index;
688     }
689     
690     private void _setIndex(int index)
691     {
692         // save child state
693         //_saveChildState();
694         if (index < -1)
695         {
696             throw new IllegalArgumentException("rowIndex is less than -1");
697         }
698 
699         if (_index == index)
700         {
701             return;
702         }
703 
704         FacesContext facesContext = getFacesContext();
705 
706         if (_index == -1)
707         {
708             if (_initialDescendantComponentState == null)
709             {
710                 // Create a template that can be used to initialise any row
711                 // that we haven't visited before, ie a "saved state" that can
712                 // be pushed to the "restoreState" method of all the child
713                 // components to set them up to represent a clean row.
714                 _initialDescendantComponentState = saveDescendantComponentStates(this, true, true);
715             }
716         }
717         else
718         {
719             // If no initial component state, there are no EditableValueHolder instances,
720             // and that means there is no state to be saved for the current row, so we can
721             // skip row state saving code safely.
722             if (_initialDescendantComponentState != null)
723             {
724                 // We are currently positioned on some row, and are about to
725                 // move off it, so save the (partial) state of the components
726                 // representing the current row. Later if this row is revisited
727                 // then we can restore this state.
728                 Collection<Object[]> savedRowState = saveDescendantComponentStates(this, true, true);
729                 if (savedRowState != null)
730                 {
731                     _rowStates.put(getContainerClientId(facesContext), savedRowState);
732                 }
733             }
734         }
735 
736         _index = index;
737         
738         DataModel<?> localModel = getDataModel();
739         localModel.setRowIndex(index);
740 
741         if (_index != -1)
742         {
743             String var = getVar();
744             if (var != null && localModel.isRowAvailable())
745             {
746                 getFacesContext().getExternalContext().getRequestMap()
747                         .put(var, localModel.getRowData());
748             }
749             String varStatus = getVarStatus();
750             if (varStatus != null)
751             {
752                 getFacesContext().getExternalContext().getRequestMap()
753                         .put(varStatus, _getRepeatStatus());
754             }
755         }
756 
757         // restore child state
758         //_restoreChildState();
759         
760         if (_index == -1)
761         {
762             // reset components to initial state
763             // If no initial state, skip row restore state code
764             if (_initialDescendantComponentState != null)
765             {
766                 restoreDescendantComponentStates(this, true, _initialDescendantComponentState, true);
767             }
768             else
769             {
770                 restoreDescendantComponentWithoutRestoreState(this, true, true);
771             }
772         }
773         else
774         {
775             Object rowState = _rowStates.get(getContainerClientId(facesContext));
776             if (rowState == null)
777             {
778                 // We haven't been positioned on this row before, so just
779                 // configure the child components of this component with
780                 // the standard "initial" state
781                 // If no initial state, skip row restore state code
782                 if (_initialDescendantComponentState != null)
783                 {
784                     restoreDescendantComponentStates(this, true, _initialDescendantComponentState, true);
785                 }
786                 else
787                 {
788                     restoreDescendantComponentWithoutRestoreState(this, true, true);
789                 }
790             }
791             else
792             {
793                 // We have been positioned on this row before, so configure
794                 // the child components of this component with the (partial)
795                 // state that was previously saved. Fields not in the
796                 // partial saved state are left with their original values.
797                 restoreDescendantComponentStates(this, true, rowState, true);
798             }
799         }
800     }
801     
802     /**
803      * Calculates the count value for the given index.
804      * @param index
805      * @return
806      */
807     private int _calculateCountForIndex(int index)
808     {
809         return (index - getOffset()) / getStep();
810     }
811 
812     private void _validateAttributes() throws FacesException
813     {
814         int begin = getOffset();
815         int end = getDataModel().getRowCount();
816         int size = getSize();
817         int step = getStep();
818         boolean sizeIsEnd = false;
819 
820         if (size == -1)
821         {
822             size = end;
823             sizeIsEnd = true;
824         }
825 
826         if (end >= 0)
827         {
828             if (size < 0)
829             {
830                 throw new FacesException("iteration size cannot be less " +
831                         "than zero");
832             }
833 
834             else if (!sizeIsEnd && (begin + size) > end)
835             {
836                 throw new FacesException("iteration size cannot be greater " +
837                         "than collection size");
838             }
839         }
840 
841         if ((size > -1) && (begin > end))
842         {
843             throw new FacesException("iteration offset cannot be greater " +
844                     "than collection size");
845         }
846 
847         if (step == -1)
848         {
849             setStep(1);
850         }
851 
852         if (step < 0)
853         {
854             throw new FacesException("iteration step size cannot be less " +
855                     "than zero");
856         }
857 
858         else if (step == 0)
859         {
860             throw new FacesException("iteration step size cannot be equal " +
861                     "to zero");
862         }
863 
864         _end = size;
865         //_step = step;
866     }
867 
868     public void process(FacesContext faces, PhaseId phase)
869     {
870         // stop if not rendered
871         if (!isRendered())
872         {
873             return;
874         }
875         
876         // validate attributes
877         _validateAttributes();
878         
879         // reset index
880         _captureScopeValues();
881         _setIndex(-1);
882 
883         try
884         {
885             // has children
886             if (getChildCount() > 0)
887             {
888                 int i = getOffset();
889                 int end = getSize();
890                 int step = getStep();
891                 end = (end >= 0) ? i + end : Integer.MAX_VALUE - 1;
892                 
893                 // grab renderer
894                 String rendererType = getRendererType();
895                 Renderer renderer = null;
896                 if (rendererType != null)
897                 {
898                     renderer = getRenderer(faces);
899                 }
900                 
901                 _count = 0;
902                 
903                 _setIndex(i);
904                 while (i < end && _isIndexAvailable())
905                 {
906 
907                     if (PhaseId.RENDER_RESPONSE.equals(phase) && renderer != null)
908                     {
909                         renderer.encodeChildren(faces, this);
910                     }
911                     else
912                     {
913                         for (int j = 0, childCount = getChildCount(); j < childCount; j++)
914                         {
915                             UIComponent child = getChildren().get(j);
916                             if (PhaseId.APPLY_REQUEST_VALUES.equals(phase))
917                             {
918                                 child.processDecodes(faces);
919                             }
920                             else if (PhaseId.PROCESS_VALIDATIONS.equals(phase))
921                             {
922                                 child.processValidators(faces);
923                             }
924                             else if (PhaseId.UPDATE_MODEL_VALUES.equals(phase))
925                             {
926                                 child.processUpdates(faces);
927                             }
928                             else if (PhaseId.RENDER_RESPONSE.equals(phase))
929                             {
930                                 child.encodeAll(faces);
931                             }
932                         }
933                     }
934                     
935                     ++_count;
936                     
937                     i += step;
938                     
939                     _setIndex(i);
940                 }
941             }
942         }
943         catch (IOException e)
944         {
945             throw new FacesException(e);
946         }
947         finally
948         {
949             _setIndex(-1);
950             _restoreScopeValues();
951         }
952     }
953 
954     @Override
955     public boolean invokeOnComponent(FacesContext context, String clientId,
956             ContextCallback callback) throws FacesException
957     {
958         if (context == null || clientId == null || callback == null)
959         {
960             throw new NullPointerException();
961         }
962         
963         final String baseClientId = getClientId(context);
964 
965         // searching for this component?
966         boolean returnValue = baseClientId.equals(clientId);
967 
968         boolean isCachedFacesContext = isTemporalFacesContext();
969         if (!isCachedFacesContext)
970         {
971             setTemporalFacesContext(context);
972         }
973 
974         pushComponentToEL(context, this);
975         try
976         {
977             if (returnValue)
978             {
979                 try
980                 {
981                     callback.invokeContextCallback(context, this);
982                     return true;
983                 }
984                 catch (Exception e)
985                 {
986                     throw new FacesException(e);
987                 }
988             }
989     
990             // Now Look throught facets on this UIComponent
991             if (this.getFacetCount() > 0)
992             {
993                 for (Iterator<UIComponent> it = this.getFacets().values().iterator(); !returnValue && it.hasNext();)
994                 {
995                     returnValue = it.next().invokeOnComponent(context, clientId, callback);
996                 }
997             }
998     
999             if (returnValue)
1000             {
1001                 return returnValue;
1002             }
1003             
1004             // is the component an inner component?
1005             if (clientId.startsWith(baseClientId))
1006             {
1007                 // Check if the clientId for the component, which we 
1008                 // are looking for, has a rowIndex attached
1009                 char separator = UINamingContainer.getSeparatorChar(context);
1010                 String subId = clientId.substring(baseClientId.length() + 1);
1011                 //If the char next to baseClientId is the separator one and
1012                 //the subId matches the regular expression
1013                 if (clientId.charAt(baseClientId.length()) == separator && 
1014                         subId.matches("[0-9]+"+separator+".*"))
1015                 {
1016                     String clientRow = subId.substring(0, subId.indexOf(separator));
1017         
1018                     // safe the current index, count aside
1019                     final int prevIndex = _index;
1020                     final int prevCount = _count;
1021                     
1022                     try
1023                     {
1024                         int invokeIndex = Integer.parseInt(clientRow);
1025                         // save the current scope values and set the right index
1026                         _captureScopeValues();
1027                         if (invokeIndex != -1)
1028                         {
1029                             // calculate count for RepeatStatus
1030                             _count = _calculateCountForIndex(invokeIndex);
1031                         }
1032                         _setIndex(invokeIndex);
1033                         
1034                         if (!_isIndexAvailable())
1035                         {
1036                             return false;
1037                         }
1038                         
1039                         for (Iterator<UIComponent> it1 = getChildren().iterator(); 
1040                             !returnValue && it1.hasNext();)
1041                         {
1042                             //recursive call to find the component
1043                             returnValue = it1.next().invokeOnComponent(context, clientId, callback);
1044                         }
1045                     }
1046                     finally
1047                     {
1048                         // restore the previous count, index and scope values
1049                         _count = prevCount;
1050                         _setIndex(prevIndex);
1051                         _restoreScopeValues();
1052                     }
1053                 }
1054                 else
1055                 {
1056                     // Searching for this component's children
1057                     if (this.getChildCount() > 0)
1058                     {
1059                         // Searching for this component's children/facets
1060                         for (Iterator<UIComponent> it = this.getChildren().iterator(); !returnValue && it.hasNext();)
1061                         {
1062                             returnValue = it.next().invokeOnComponent(context, clientId, callback);
1063                         }
1064                     }
1065                 }
1066             }
1067         }
1068         finally
1069         {
1070             //all components must call popComponentFromEl after visiting is finished
1071             popComponentFromEL(context);
1072             if (!isCachedFacesContext)
1073             {
1074                 setTemporalFacesContext(null);
1075             }
1076         }
1077 
1078         return returnValue;
1079     }
1080     
1081     @Override
1082     protected FacesContext getFacesContext()
1083     {
1084         if (_facesContext == null)
1085         {
1086             return super.getFacesContext();
1087         }
1088         else
1089         {
1090             return _facesContext;
1091         }
1092     }
1093     
1094     private boolean isTemporalFacesContext()
1095     {
1096         return _facesContext != null;
1097     }
1098     
1099     private void setTemporalFacesContext(FacesContext facesContext)
1100     {
1101         _facesContext = facesContext;
1102     }
1103 
1104     @Override
1105     public boolean visitTree(VisitContext context, VisitCallback callback)
1106     {
1107         // override the behavior from UIComponent to visit
1108         // all children once per "row"
1109         
1110         //(Boolean) context.getFacesContext().getAttributes().get(SKIP_ITERATION_HINT);
1111         Boolean skipIterationHint = context.getHints().contains(VisitHint.SKIP_ITERATION);
1112         if (skipIterationHint != null && skipIterationHint.booleanValue())
1113         {
1114             return super.visitTree(context, callback);
1115         }
1116         
1117         if (!isVisitable(context)) 
1118         {
1119             return false;
1120         }
1121         
1122         // save the current index, count aside
1123         final int prevIndex = _index;
1124         final int prevCount = _count;
1125         
1126         // validate attributes
1127         _validateAttributes();
1128         
1129         // reset index and save scope values
1130         _captureScopeValues();
1131         _setIndex(-1);
1132         
1133         // push the Component to EL
1134         pushComponentToEL(context.getFacesContext(), this);
1135         try 
1136         {
1137             VisitResult res = context.invokeVisitCallback(this, callback);
1138             switch (res) 
1139             {
1140             // we are done, nothing has to be processed anymore
1141             case COMPLETE:
1142                 return true;
1143 
1144             case REJECT:
1145                 return false;
1146 
1147             //accept
1148             default:
1149                 // determine if we need to visit our children
1150                 // Note that we need to do this check because we are a NamingContainer
1151                 Collection<String> subtreeIdsToVisit = context
1152                         .getSubtreeIdsToVisit(this);
1153                 boolean doVisitChildren = subtreeIdsToVisit != null
1154                         && !subtreeIdsToVisit.isEmpty();
1155                 if (doVisitChildren)
1156                 {
1157                     // visit the facets of the component
1158                     if (getFacetCount() > 0) 
1159                     {
1160                         for (UIComponent facet : getFacets().values()) 
1161                         {
1162                             if (facet.visitTree(context, callback)) 
1163                             {
1164                                 return true;
1165                             }
1166                         }
1167                     }
1168                     
1169                     // visit the children once per "row"
1170                     if (getChildCount() > 0) 
1171                     {
1172                         int i = getOffset();
1173                         int end = getSize();
1174                         int step = getStep();
1175                         end = (end >= 0) ? i + end : Integer.MAX_VALUE - 1;
1176                         _count = 0;
1177                         
1178                         _setIndex(i);
1179                         while (i < end && _isIndexAvailable())
1180                         {
1181                             for (int j = 0, childCount = getChildCount(); j < childCount; j++)
1182                             {
1183                                 UIComponent child = getChildren().get(j);
1184                                 if (child.visitTree(context, callback)) 
1185                                 {
1186                                     return true;
1187                                 }
1188                             }
1189                             
1190                             _count++;
1191                             i += step;
1192                             
1193                             _setIndex(i);
1194                         }
1195                     }
1196                 }
1197                 return false;
1198             }
1199         }
1200         finally 
1201         {
1202             // pop the component from EL
1203             popComponentFromEL(context.getFacesContext());
1204             
1205             // restore the previous count, index and scope values
1206             _count = prevCount;
1207             _setIndex(prevIndex);
1208             _restoreScopeValues();
1209         }
1210     }
1211 
1212     @Override
1213     public void processDecodes(FacesContext faces)
1214     {
1215         if (!isRendered())
1216         {
1217             return;
1218         }
1219         
1220         process(faces, PhaseId.APPLY_REQUEST_VALUES);
1221         decode(faces);
1222     }
1223 
1224     @Override
1225     public void processUpdates(FacesContext faces)
1226     {
1227         if (!isRendered())
1228         {
1229             return;
1230         }
1231         
1232         process(faces, PhaseId.UPDATE_MODEL_VALUES);
1233         
1234         if (faces.getRenderResponse())
1235         {
1236             _isValidChilds = false;
1237         }
1238     }
1239 
1240     @Override
1241     public void processValidators(FacesContext faces)
1242     {
1243         if (!isRendered())
1244         {
1245             return;
1246         }
1247         
1248         process(faces, PhaseId.PROCESS_VALIDATIONS);
1249         
1250         // check if an validation error forces the render response for our data
1251         if (faces.getRenderResponse())
1252         {
1253             _isValidChilds = false;
1254         }
1255     }
1256 
1257     // from RI
1258     private final static class SavedState implements Serializable
1259     {
1260         private boolean _localValueSet;
1261         private Object _submittedValue;
1262         private boolean _valid = true;
1263         private Object _value;
1264 
1265         private static final long serialVersionUID = 2920252657338389849L;
1266         
1267         public SavedState(EditableValueHolder evh)
1268         {
1269             _value = evh.getLocalValue();
1270             _localValueSet = evh.isLocalValueSet();
1271             _valid = evh.isValid();
1272             _submittedValue = evh.getSubmittedValue();
1273         }        
1274 
1275         Object getSubmittedValue()
1276         {
1277             return (_submittedValue);
1278         }
1279 
1280         void setSubmittedValue(Object submittedValue)
1281         {
1282             _submittedValue = submittedValue;
1283         }
1284 
1285         boolean isValid()
1286         {
1287             return (_valid);
1288         }
1289 
1290         void setValid(boolean valid)
1291         {
1292             _valid = valid;
1293         }
1294 
1295         Object getValue()
1296         {
1297             return _value;
1298         }
1299 
1300         public void setValue(Object value)
1301         {
1302             _value = value;
1303         }
1304 
1305         boolean isLocalValueSet()
1306         {
1307             return _localValueSet;
1308         }
1309 
1310         public void setLocalValueSet(boolean localValueSet)
1311         {
1312             _localValueSet = localValueSet;
1313         }
1314 
1315         @Override
1316         public String toString()
1317         {
1318             return ("submittedValue: " + _submittedValue + " value: " + _value + " localValueSet: " + _localValueSet);
1319         }
1320         
1321         public void restoreState(EditableValueHolder evh)
1322         {
1323             evh.setValue(_value);
1324             evh.setValid(_valid);
1325             evh.setSubmittedValue(_submittedValue);
1326             evh.setLocalValueSet(_localValueSet);
1327         }
1328 
1329         public void populate(EditableValueHolder evh)
1330         {
1331             _value = evh.getLocalValue();
1332             _valid = evh.isValid();
1333             _submittedValue = evh.getSubmittedValue();
1334             _localValueSet = evh.isLocalValueSet();
1335         }
1336 
1337         public void apply(EditableValueHolder evh)
1338         {
1339             evh.setValue(_value);
1340             evh.setValid(_valid);
1341             evh.setSubmittedValue(_submittedValue);
1342             evh.setLocalValueSet(_localValueSet);
1343         }
1344     }
1345 
1346     private final class IndexedEvent extends FacesEvent
1347     {
1348         private final FacesEvent _target;
1349 
1350         private final int _index;
1351 
1352         public IndexedEvent(UIRepeat owner, FacesEvent target, int index)
1353         {
1354             super(owner);
1355             _target = target;
1356             _index = index;
1357         }
1358 
1359         @Override
1360         public PhaseId getPhaseId()
1361         {
1362             return _target.getPhaseId();
1363         }
1364 
1365         @Override
1366         public void setPhaseId(PhaseId phaseId)
1367         {
1368             _target.setPhaseId(phaseId);
1369         }
1370 
1371         public boolean isAppropriateListener(FacesListener listener)
1372         {
1373             return _target.isAppropriateListener(listener);
1374         }
1375 
1376         public void processListener(FacesListener listener)
1377         {
1378             UIRepeat owner = (UIRepeat) getComponent();
1379             
1380             // safe the current index, count aside
1381             final int prevIndex = owner._index;
1382             final int prevCount = owner._count;
1383             
1384             try
1385             {
1386                 owner._captureScopeValues();
1387                 if (this._index != -1)
1388                 {
1389                     // calculate count for RepeatStatus
1390                     _count = _calculateCountForIndex(this._index);
1391                 }
1392                 owner._setIndex(this._index);
1393                 if (owner._isIndexAvailable())
1394                 {
1395                     _target.processListener(listener);
1396                 }
1397             }
1398             finally
1399             {
1400                 // restore the previous count, index and scope values
1401                 owner._count = prevCount;
1402                 owner._setIndex(prevIndex);
1403                 owner._restoreScopeValues();
1404             }
1405         }
1406 
1407         public int getIndex()
1408         {
1409             return _index;
1410         }
1411 
1412         public FacesEvent getTarget()
1413         {
1414             return _target;
1415         }
1416 
1417     }
1418 
1419     @Override
1420     public void broadcast(FacesEvent event) throws AbortProcessingException
1421     {
1422         if (event instanceof IndexedEvent)
1423         {
1424             IndexedEvent idxEvent = (IndexedEvent) event;
1425             
1426             // safe the current index, count aside
1427             final int prevIndex = _index;
1428             final int prevCount = _count;
1429             
1430             try
1431             {
1432                 _captureScopeValues();
1433                 if (idxEvent.getIndex() != -1)
1434                 {
1435                     // calculate count for RepeatStatus
1436                     _count = _calculateCountForIndex(idxEvent.getIndex());
1437                 }
1438                 _setIndex(idxEvent.getIndex());
1439                 if (_isIndexAvailable())
1440                 {
1441                     // get the target FacesEvent
1442                     FacesEvent target = idxEvent.getTarget();
1443                     FacesContext facesContext = getFacesContext();
1444                     
1445                     // get the component associated with the target event and push
1446                     // it and its composite component parent, if available, to the
1447                     // component stack to have them available while processing the 
1448                     // event (see also UIViewRoot._broadcastAll()).
1449                     UIComponent targetComponent = target.getComponent();
1450                     UIComponent compositeParent = UIComponent
1451                             .getCompositeComponentParent(targetComponent);
1452                     if (compositeParent != null)
1453                     {
1454                         pushComponentToEL(facesContext, compositeParent);
1455                     }
1456                     pushComponentToEL(facesContext, targetComponent);
1457                     
1458                     try
1459                     {
1460                         // actual event broadcasting
1461                         targetComponent.broadcast(target);
1462                     }
1463                     finally
1464                     {
1465                         // remove the components from the stack again
1466                         popComponentFromEL(facesContext);
1467                         if (compositeParent != null)
1468                         {
1469                             popComponentFromEL(facesContext);
1470                         }
1471                     }
1472                 }
1473             }
1474             finally
1475             {
1476                 // restore the previous count, index and scope values
1477                 _count = prevCount;
1478                 _setIndex(prevIndex);
1479                 _restoreScopeValues();
1480             }
1481         }
1482         else
1483         {
1484             super.broadcast(event);
1485         }
1486     }
1487 
1488     @Override
1489     public void queueEvent(FacesEvent event)
1490     {
1491         super.queueEvent(new IndexedEvent(this, event, _index));
1492     }
1493 
1494     // -=Leonardo Uribe=- At the moment I haven't found any use case that
1495     // require to store the rowStates in the component state, mostly
1496     // because EditableValueHolder instances render the value into the
1497     // client and then this value are taken back at the beginning of the
1498     // next request. So, I just let this code in comments just in case
1499     // somebody founds an issue with this.  
1500     /* 
1501     @SuppressWarnings("unchecked")
1502     @Override
1503     public void restoreState(FacesContext facesContext, Object state)
1504     {
1505         if (state == null)
1506         {
1507             return;
1508         }
1509         
1510         Object[] values = (Object[])state;
1511         super.restoreState(facesContext,values[0]);
1512         if (values[1] == null)
1513         {
1514             _rowStates.clear();
1515         }
1516         else
1517         {
1518             _rowStates = (Map<String, Collection<Object[]>>) restoreAttachedState(facesContext, values[1]);
1519         }
1520     }
1521 
1522     @Override
1523     public Object saveState(FacesContext facesContext)
1524     {
1525         if (initialStateMarked())
1526         {
1527             Object parentSaved = super.saveState(facesContext);
1528             if (parentSaved == null && _rowStates.isEmpty())
1529             {
1530                 //No values
1531                 return null;
1532             }   
1533             return new Object[]{parentSaved, saveAttachedState(facesContext, _rowStates)};
1534         }
1535         else
1536         {
1537             Object[] values = new Object[2];
1538             values[0] = super.saveState(facesContext);
1539             values[1] = saveAttachedState(facesContext, _rowStates);
1540             return values;
1541         } 
1542     }
1543     */
1544     
1545     @Override
1546     public void encodeBegin(FacesContext context) throws IOException
1547     {
1548         _initialDescendantComponentState = null;
1549         if (_isValidChilds && !hasErrorMessages(context))
1550         {
1551             // Clear the data model so that when rendering code calls
1552             // getDataModel a fresh model is fetched from the backing
1553             // bean via the value-binding.
1554             _dataModelMap.clear();
1555 
1556             // When the data model is cleared it is also necessary to
1557             // clear the saved row state, as there is an implicit 1:1
1558             // relation between objects in the _rowStates and the
1559             // corresponding DataModel element.
1560             _rowStates.clear();
1561         }
1562         // TODO Auto-generated method stub
1563         super.encodeBegin(context);
1564     }
1565     
1566     private boolean hasErrorMessages(FacesContext context)
1567     {
1568         for (Iterator<FacesMessage> iter = context.getMessages(); iter.hasNext();)
1569         {
1570             FacesMessage message = iter.next();
1571             if (FacesMessage.SEVERITY_ERROR.compareTo(message.getSeverity()) <= 0)
1572             {
1573                 return true;
1574             }
1575         }
1576         return false;
1577     }
1578 
1579     @Override
1580     public void encodeChildren(FacesContext faces) throws IOException
1581     {
1582         if (!isRendered())
1583         {
1584             return;
1585         }
1586         
1587         process(faces, PhaseId.RENDER_RESPONSE);
1588     }
1589 
1590     @Override
1591     public boolean getRendersChildren()
1592     {
1593         if (getRendererType() != null)
1594         {
1595             Renderer renderer = getRenderer(getFacesContext());
1596             if (renderer != null)
1597             {
1598                 return renderer.getRendersChildren();
1599             }
1600         }
1601         
1602         return true;
1603     }
1604     
1605     enum PropertyKeys
1606     {
1607          value
1608         , var
1609         , size
1610         , varStatus
1611         , offset
1612         , step
1613     }
1614 }