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