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