View Javadoc

1   // WARNING: This file was automatically generated. Do not edit it directly,
2   //          or you will lose your changes.
3   
4   /*
5    * Licensed to the Apache Software Foundation (ASF) under one
6    * or more contributor license agreements.  See the NOTICE file
7    * distributed with this work for additional information
8    * regarding copyright ownership.  The ASF licenses this file
9    * to you under the Apache License, Version 2.0 (the
10   * "License"); you may not use this file except in compliance
11   * with the License.  You may obtain a copy of the License at
12   *
13   *   http://www.apache.org/licenses/LICENSE-2.0
14   *
15   * Unless required by applicable law or agreed to in writing,
16   * software distributed under the License is distributed on an
17   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18   * KIND, either express or implied.  See the License for the
19   * specific language governing permissions and limitations
20   * under the License.
21  */
22  package org.apache.myfaces.trinidad.component;
23  
24  import java.io.IOException;
25  import java.util.AbstractMap;
26  import java.util.Collection;
27  import java.util.Collections;
28  import java.util.HashSet;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  import javax.faces.component.UIComponent;
33  import javax.faces.component.visit.VisitCallback;
34  import javax.faces.component.visit.VisitContext;
35  import javax.faces.context.FacesContext;
36  import javax.faces.event.AbortProcessingException;
37  import javax.faces.event.PhaseId;
38  import javax.faces.render.Renderer;
39  import org.apache.myfaces.trinidad.bean.FacesBean;
40  import org.apache.myfaces.trinidad.bean.PropertyKey;
41  import org.apache.myfaces.trinidad.model.CollectionModel;
42  import org.apache.myfaces.trinidad.model.LocalRowKeyIndex;
43  import org.apache.myfaces.trinidad.model.ModelUtils;
44  import org.apache.myfaces.trinidad.render.ClientRowKeyManager;
45  import org.apache.myfaces.trinidad.util.ComponentUtils;
46  
47  /**
48   *
49   * <p>UIXIterator is a component that performs iteration over its child components. It is similar to UIXTable
50   * but has no chrome.
51   *  Each child is repeatedly stamped as many times as necessary.
52   *  Iteration is done starting at the index given by the &quot;first&quot; attribute,
53   *  for as many indices as specified by the &quot;rows&quot; attribute.
54   *  If &quot;rows&quot; returns 0, then the iteration continues until 
55   *  there are no more elements in the underlying data.
56   *  </p>
57   *  <p> 
58   *   While the &lt;tr:forEach&gt;
59   * will be sufficient for most user's needs, it does not work with a JSF
60   * DataModel, or CollectionModel. It also cannot be bound to EL expressions that
61   * use component-managed EL variables
62   * (such as the &quot;var&quot; variable on an &lt;tr:table&gt;), because
63   * a forEach tag runs during
64   * The &lt;tr:iterator&gt; tag was created to
65   * address these issues.
66   * </p>
67   * <p>
68   * To list all, the benefits of UIXIterator over forEach:
69   * <ul>
70   * <li>Access to component-managed EL variables</li>
71   * <li>Full support for CollectionModel and DataModel</li>
72   * <li>Does not require creating multiple copies of children,
73   * so more memory efficient</li>
74   * <li>Much better at dealing with adding and deleting children,
75   * at least when used with a CollectionModel with a good
76   * implementation of getRowKey()</li>
77   * <li>Supports "binding", and all other forms of JSF component
78   * manipulation</li>
79   * </ul>
80   * and the negative aspects:
81   * <ul>
82   * <li>Leaves behind a component in the hierarchy, which causes
83   * problems with components like panelFormLayout that try to handle each child
84   * individually.</li>
85   * <li>Because there's only one of each child, the same limitations
86   * on "binding", etc., as apply inside a table also apply to iterator.</li>
87   * </ul>
88   * </p>
89   * <p>By default, it processes up to 25 rows. Use the rows attribute to alter this behavior.</p>
90   *
91   * <h4>Events:</h4>
92   * <table border="1" width="100%" cellpadding="3" summary="">
93   * <tr bgcolor="#CCCCFF" class="TableHeadingColor">
94   * <th align="left">Type</th>
95   * <th align="left">Phases</th>
96   * <th align="left">Description</th>
97   * </tr>
98   * <tr class="TableRowColor">
99   * <td valign="top"><code>org.apache.myfaces.trinidad.event.AttributeChangeEvent</code></td>
100  * <td valign="top" nowrap>Invoke<br>Application<br>Apply<br>Request<br>Values</td>
101  * <td valign="top">Event delivered to describe an attribute change.  Attribute change events are not delivered for any programmatic change to a property.  They are only delivered when a renderer changes a property without the application's specific request.  An example of an attribute change event might include the width of a column that supported client-side resizing.</td>
102  * </tr>
103  * </table>
104  */
105 public class UIXIterator extends UIXCollection
106                          implements LocalRowKeyIndex,
107                                     FlattenedComponent
108 {
109   static public final FacesBean.Type TYPE = new FacesBean.Type(
110     UIXCollection.TYPE);
111   static public final PropertyKey VAR_STATUS_KEY =
112     TYPE.registerKey("varStatus", String.class, PropertyKey.CAP_NOT_BOUND);
113   static public final PropertyKey VALUE_KEY =
114     TYPE.registerKey("value");
115   static public final PropertyKey ROWS_KEY =
116     TYPE.registerKey("rows", Integer.class, Integer.valueOf(25));
117   static public final PropertyKey FIRST_KEY =
118     TYPE.registerKey("first", Integer.class, Integer.valueOf(0));
119 
120   static public final String COMPONENT_FAMILY =
121     "org.apache.myfaces.trinidad.Iterator";
122   static public final String COMPONENT_TYPE =
123     "org.apache.myfaces.trinidad.Iterator";
124 
125   /**
126    * Construct an instance of the UIXIterator.
127    */
128   public UIXIterator()
129   {
130     super(null);
131   }
132   
133 
134   /**
135    * Override to return true.
136    */
137   @Override
138   public boolean getRendersChildren()
139   {
140     return true;
141   }
142 
143   /**
144    * Sets up the iteration context for each child and processes it
145    */
146   public <S> boolean processFlattenedChildren(
147     final FacesContext context,
148     ComponentProcessingContext cpContext,
149     final ComponentProcessor<S> childProcessor,
150     final S callbackContext) throws IOException
151   {
152     boolean processedChildren;
153 
154     setupFlattenedContext(context, cpContext);
155 
156     try
157     {
158       // Mimic what would normally happen in the non-flattening case for encodeBegin():
159       __processFlattenedChildrenBegin();
160 
161       setupFlattenedChildrenContext(context, cpContext);
162 
163       try
164       {
165         Runner runner = new IndexedRunner(cpContext)
166         {
167           @Override
168           protected void process(UIComponent kid, ComponentProcessingContext cpContext) throws IOException
169           {
170             kid.pushComponentToEL(context, null);
171 
172             try
173             {
174               childProcessor.processComponent(context, cpContext, kid, callbackContext);
175             }
176             finally
177             {
178               kid.popComponentFromEL(context);
179             }
180           }
181         };
182 
183         processedChildren = runner.run();
184         Exception exp = runner.getException();
185         if (exp != null)
186         {
187           if (exp instanceof RuntimeException)
188             throw (RuntimeException) exp;
189 
190           if (exp instanceof IOException)
191             throw (IOException) exp;
192           throw new IllegalStateException(exp);
193         }
194       }
195       finally
196       {
197         tearDownFlattenedChildrenContext(context, cpContext);
198       }
199     }
200     finally
201     {
202       tearDownFlattenedContext(context, cpContext);
203     }
204 
205     return processedChildren;
206   }
207 
208   /**
209    * Returns <code>true</code> if this FlattenedComponent is currently flattening its children
210    * @param context FacesContext
211    * @return <code>true</code> if this FlattenedComponent is currently flattening its children
212    */
213   public boolean isFlatteningChildren(FacesContext context)
214   {
215     // if we don't have a Renderer, then we're flattening
216     return (getRendererType() == null);
217   }
218 
219   /**
220    * Repeatedly render the children as many times as needed.
221    */
222   @Override
223   public void encodeChildren(final FacesContext context)
224     throws IOException
225   {
226     if (!isRendered())
227       return;
228 
229     // if this is the table there will be a rendererType:
230     if (getRendererType() != null)
231     {
232       Renderer renderer = getRenderer(context);
233       if (renderer != null)
234       {
235         renderer.encodeChildren(context, this);
236       }
237     }
238     else // this is not the table. it must be the iterator
239     {
240       Runner runner = new IndexedRunner()
241       {
242         @Override
243         protected void process(
244           UIComponent                kid,
245           ComponentProcessingContext cpContext
246           ) throws IOException
247         {
248           kid.encodeAll(context);
249         }
250       };
251       runner.run();
252       Exception exp = runner.getException();
253       if (exp != null)
254       {
255         if (exp instanceof RuntimeException)
256           throw (RuntimeException) exp;
257 
258         if (exp instanceof IOException)
259           throw (IOException) exp;
260         throw new IllegalStateException(exp);
261       }
262     }
263   }
264 
265   /**
266    * Enhances the varStatusMap created by the super class to include:<ul>
267    * <li>begin - the index of the first row being rendered
268    * <li>first - true if the current row is the first row
269    * <li>count - indicates which iteration this is. This always starts at one,
270    * and increases (by one) as the loop progresses.
271    * <li>step - this is always one.
272    * </ul>
273    */
274   @Override
275   protected Map<String, Object> createVarStatusMap()
276   {
277     final Map<String, Object> map = super.createVarStatusMap();
278     return new AbstractMap<String, Object>()
279     {
280       @Override
281       public Object get(Object key)
282       {
283         // some of these keys are from <c:forEach>, ie:
284         // javax.servlet.jsp.jstl.core.LoopTagStatus
285         if ("begin".equals(key)) // from jstl
286         {
287           return Integer.valueOf(getFirst());
288         }
289         if ("first".equals(key)) // from jstl
290         {
291           boolean isFirst = (getFirst() == getRowIndex());
292           return Boolean.valueOf(isFirst);
293         }
294         if ("count".equals(key)) // from jstl
295         {
296           int count = getRowIndex() - getFirst() + 1;
297           return Integer.valueOf(count);
298         }
299         if ("step".equals(key)) // from jstl
300         {
301           return Integer.valueOf(1);
302         }
303         return map.get(key);
304       }
305 
306       @Override
307       public Set<Map.Entry<String, Object>> entrySet()
308       {
309         return map.entrySet();
310       }
311     };
312   }
313 
314   @Override
315   protected CollectionModel createCollectionModel(
316     CollectionModel current,
317     Object value)
318   {
319     CollectionModel model = ModelUtils.toCollectionModel(value);
320     // initialize to -1. we need to do this incase some application logic
321     // changed this index. Also, some JSF1.0 RI classes were initially starting
322     // with a rowIndex of 0.
323     // we need this to be -1 because of name-transformation.
324     model.setRowIndex(-1);
325     assert model.getRowIndex() == -1 : "RowIndex did not reset to -1";
326     return model;
327   }
328 
329   @Override
330   protected void processFacetsAndChildren(
331     final FacesContext context,
332     final PhaseId phaseId)
333   {
334     Runner runner = new IndexedRunner()
335     {
336       @Override
337       protected void process(UIComponent kid, ComponentProcessingContext cpContext)
338       {
339         UIXIterator.this.processComponent(context, kid, phaseId);
340       }
341     };
342     runner.run();
343   }
344 
345   // Extract the current row token from the clientId
346   private String _getClientToken(String clientIdPrefix, String cellClientId)
347   {
348     int tokenStartIndex = clientIdPrefix.length() + 1;
349     int tokenEndIndex = cellClientId.indexOf(':', tokenStartIndex);
350 
351     if (tokenEndIndex != -1)
352     {
353       return cellClientId.substring(tokenStartIndex, tokenEndIndex);
354     }
355     else
356     {
357       return null;
358     }
359   }
360 
361   @Override
362   protected boolean visitData(
363     final VisitContext  visitContext,
364     final VisitCallback visitCallback)
365   {
366     Collection<String> subtreeIds = visitContext.getSubtreeIdsToVisit(this);
367 
368     // create a special VisitContext that doesn't visit the Facets
369     // of column components since they aren't visited on each row
370     final VisitContext noColumnFacetContext = new NoColumnFacetsVisitContext(visitContext);
371 
372     // runner to use to process the rows
373     Runner runner;
374 
375     if (VisitContext.ALL_IDS.equals(subtreeIds))
376     {
377       // we're processing all of the rows, so use the indexed runner (plus, we can't call size() on
378       // the ALL_IDS collection, so we don't have a whole lot of choice here
379       runner = new IndexedRunner()
380       {
381         @Override
382         protected void process(UIComponent kid, ComponentProcessingContext cpContext)
383         {
384           if (UIXComponent.visitTree(noColumnFacetContext, kid, visitCallback))
385           {
386             throw new AbortProcessingException();
387           }
388         }
389       };
390     }
391     else
392     {
393       // We are only visiting a subset of the tree, so figure out which rows to visit
394 
395       String ourClientIdPrefix = getClientId(visitContext.getFacesContext());
396 
397       int subtreeIdCount = subtreeIds.size();
398 
399       // build up a set of the row keys to visit rather than iterating
400       // and visiting every row
401       Set<String> rowsToVisit;
402 
403       if (subtreeIdCount > 1)
404       {
405         rowsToVisit = new HashSet<String>(subtreeIdCount);
406 
407         for (String currClientId : subtreeIds)
408         {
409           String clientToken = _getClientToken(ourClientIdPrefix, currClientId);
410 
411           if (clientToken != null)
412           {
413             rowsToVisit.add(clientToken);
414           }
415         }
416       }
417       else
418       {
419         String clientToken = _getClientToken(ourClientIdPrefix,
420                                              subtreeIds.iterator().next());
421 
422         if (clientToken != null)
423         {
424           rowsToVisit = Collections.singleton(clientToken);
425         }
426         else
427         {
428           rowsToVisit = Collections.emptySet();
429         }
430       }
431 
432       // we didn't visit any data
433       if (rowsToVisit.isEmpty())
434         return false;
435 
436       // visit only the rows we need to
437       runner = new KeyedRunner(rowsToVisit)
438       {
439         @Override
440         protected void process(
441           UIComponent                kid,
442           ComponentProcessingContext cpContext
443           ) throws IOException
444         {
445           if (UIXComponent.visitTree(noColumnFacetContext, kid, visitCallback))
446           {
447             throw new AbortProcessingException();
448           }
449         }
450       };
451     }
452 
453     try
454     {
455       runner.run();
456     }
457     finally
458     {
459       return (runner.getException() instanceof AbortProcessingException);
460     }
461   }
462 
463   /**
464    * Abstract class for processing rows
465    */
466   private abstract class Runner implements ComponentProcessor<Object>
467   {
468     public Runner()
469     {
470       this(null);
471     }
472 
473     public Runner(ComponentProcessingContext cpContext)
474     {
475       _cpContext = cpContext;
476     }
477 
478     public abstract boolean run();
479 
480     /**
481      * Sets up the context for the child and processes it
482      */
483     public void processComponent(
484       FacesContext context,
485       ComponentProcessingContext cpContext,
486       UIComponent component,
487       Object callbackContext) throws IOException
488     {
489       try
490       {
491         process(component, cpContext);
492       }
493       catch (IOException ioe)
494       {
495         throw ioe;
496       }
497       catch (AbortProcessingException ape)
498       {
499         // we're done, so abort
500         _exception = ape;
501         throw ape;
502       }
503       catch (Exception e)
504       {
505         _exception = e;
506       }
507     }
508 
509     public Exception getException()
510     {
511       return _exception;
512     }
513 
514     protected abstract void process(UIComponent comp, ComponentProcessingContext cpContext)
515       throws Exception;
516 
517     protected final ComponentProcessingContext getComponentProcessingContext()
518     {
519       return _cpContext;
520     }
521 
522     public final void setException(Exception e)
523     {
524       _exception = e;
525     }
526 
527     private Exception _exception = null;
528 
529     private final ComponentProcessingContext _cpContext;
530   }
531 
532   /**
533    * Class for visiting getRows() by index rows starting getFirst()
534    */
535   private abstract class IndexedRunner extends Runner
536   {
537     public IndexedRunner()
538     {
539       this(null);
540     }
541 
542     public IndexedRunner(ComponentProcessingContext cpContext)
543     {
544       super(cpContext);
545     }
546 
547     public final boolean run()
548     {
549       FacesContext context = FacesContext.getCurrentInstance();
550       ComponentProcessingContext cpContext = getComponentProcessingContext();
551 
552       List<UIComponent> stamps = getStamps();
553       int oldIndex = getRowIndex();
554       int first = getFirst();
555       int rows = getRows();
556       int end = (rows <= 0) //show everything
557         ? Integer.MAX_VALUE
558         : first + rows;
559 
560       boolean processedChild = false;
561 
562       try
563       {
564         for(int i=first; i<end; i++)
565         {
566           setRowIndex(i);
567           if (isRowAvailable())
568           {
569             // latch processedChild the first time we process a child
570             processedChild |= (cpContext != null)
571               ? UIXComponent.processFlattenedChildren(context, cpContext, this, stamps, null)
572               : UIXComponent.processFlattenedChildren(context, this, stamps, null);
573           }
574           else
575             break;
576         }
577       }
578       catch (IOException e)
579       {
580         setException(e);
581       }
582       finally
583       {
584         setRowIndex(oldIndex);
585       }
586 
587       return processedChild;
588     }
589   }
590 
591   /**
592    * Runner that visits the rows specified by the client row key tokens
593    */
594   private abstract class KeyedRunner extends Runner
595   {
596     public KeyedRunner(Iterable<String> clientKeys)
597     {
598       super();
599       _clientKeys = clientKeys;
600     }
601 
602     public final boolean run()
603     {
604       FacesContext context = FacesContext.getCurrentInstance();
605 
606       List<UIComponent> stamps = getStamps();
607       int oldIndex = getRowIndex();
608 
609       boolean processedChild = false;
610 
611       try
612       {
613         // need to convert row key tokens to row keys
614         ClientRowKeyManager rowKeyManager = getClientRowKeyManager();
615 
616         for(String clientKey : _clientKeys)
617         {
618           Object rowKey = rowKeyManager.getRowKey(context, UIXIterator.this, clientKey);
619 
620           if (rowKey != null)
621           {
622             setRowKey(rowKey);
623             if (isRowAvailable())
624             {
625               // latch processedChild the first time we process a child
626               processedChild |= UIXComponent.processFlattenedChildren(context, this, stamps, null);
627             }
628           }
629         }
630       }
631       catch (IOException e)
632       {
633         setException(e);
634       }
635       finally
636       {
637         setRowIndex(oldIndex);
638       }
639 
640       return processedChild;
641     }
642 
643     private final Iterable<String> _clientKeys;
644   }
645 
646   @Override
647   void __encodeBegin(FacesContext context) throws IOException
648   {
649     _fixupFirst();
650     super.__encodeBegin(context);
651   }
652 
653   // make sure the current range exists on the model:
654   // see bug 4143852:
655   private void _fixupFirst()
656   {
657     int first = getFirst();
658     // if we are starting from row zero then there is no problem:
659     if (first == 0)
660       return;
661 
662     // Negative "first" makes no sense. Given the logic below,
663     // it forces iterator to scroll to the end unnecessarily.
664     if (first < 0)
665     {
666       setFirst(0);
667       return;
668     }
669 
670     CollectionModel model = getCollectionModel();
671     int oldIndex = model.getRowIndex();
672     try
673     {
674       model.setRowIndex(first);
675       // if the starting row doesn't exist then we need to scroll back:
676       if (!model.isRowAvailable())
677       {
678         int size = model.getRowCount();
679         int rows = getRows();
680         // if the rowCount is unknown OR
681         //    the blockSize is show all OR
682         //    there are fewer rows than the blockSize on the table
683         // then start from the beginning:
684         if ((size <= 0) || (rows <= 0) || (size <= rows))
685           first = 0;
686         else
687         {
688           // scroll to the last page:
689           first = size - rows;
690           model.setRowIndex(first);
691           // make sure the row is indeed available:
692           if (!model.isRowAvailable())
693           {
694             // row is not available. this happens when getRowCount() lies.
695             // Some DataModel implementations seem to have rowCount methods which
696             // lie. see bug 4157186
697             first = 0;
698           }
699         }
700         setFirst(first);
701       }
702     }
703     finally
704     {
705       model.setRowIndex(oldIndex);
706     }
707   }
708 
709   /**
710    * Gets <html>
711    *  Name of the EL variable used to reference the varStatus information.
712    *           Once this component has completed rendering, this variable is
713    *           removed (or reverted back to its previous value).
714    *           The VarStatus provides contextual information about the state of the
715    *           component to EL expressions. For components that iterate, varStatus
716    *           also provides loop counter information.  Please see the this 
717    *           component's documentation for the specific properties on the varStatus.
718    *           The common properties on varStatus include:
719    *           <ul>
720    *             <li>"model" - returns the CollectionModel for this component.</li>
721    *             <li>"index" - returns the zero based row index.</li>
722    *             <li>"hierarchicalIndex" - returns an array containing zero based row index.</li>
723    *             <li>"hierarchicalLabel" - returns a string label representing 1 based index of this row.</li>
724    *             <li>"rowKey" - returns the current rowKey in the collection.</li>
725    *             <li>"current" - returns the current row in the collection.</li>
726    *           </ul></html>
727    *
728    * @return  the new varStatus value
729    */
730   final public String getVarStatus()
731   {
732     return ComponentUtils.resolveString(getProperty(VAR_STATUS_KEY));
733   }
734 
735   /**
736    * Sets <html>
737    *  Name of the EL variable used to reference the varStatus information.
738    *           Once this component has completed rendering, this variable is
739    *           removed (or reverted back to its previous value).
740    *           The VarStatus provides contextual information about the state of the
741    *           component to EL expressions. For components that iterate, varStatus
742    *           also provides loop counter information.  Please see the this 
743    *           component's documentation for the specific properties on the varStatus.
744    *           The common properties on varStatus include:
745    *           <ul>
746    *             <li>"model" - returns the CollectionModel for this component.</li>
747    *             <li>"index" - returns the zero based row index.</li>
748    *             <li>"hierarchicalIndex" - returns an array containing zero based row index.</li>
749    *             <li>"hierarchicalLabel" - returns a string label representing 1 based index of this row.</li>
750    *             <li>"rowKey" - returns the current rowKey in the collection.</li>
751    *             <li>"current" - returns the current row in the collection.</li>
752    *           </ul></html>
753    * 
754    * @param varStatus  the new varStatus value
755    */
756   final public void setVarStatus(String varStatus)
757   {
758     setProperty(VAR_STATUS_KEY, (varStatus));
759   }
760 
761   /**
762    * Gets the data model being used by this component.
763    * The specific model class is
764    *         <code>org.apache.myfaces.trinidad.model.CollectionModel</code>.
765    * 
766    *         You may also use other model instances, e.g.,  
767    *         <code>java.util.List</code>  ,
768    *         array, and  <code>javax.faces.model.DataModel</code>.
769    *         This component will automatically convert the instance
770    *         into a <code>CollectionModel</code>.
771    *
772    * @return  the new value value
773    */
774   final public Object getValue()
775   {
776     return getProperty(VALUE_KEY);
777   }
778 
779   /**
780    * Sets the data model being used by this component.
781    * The specific model class is
782    *         <code>org.apache.myfaces.trinidad.model.CollectionModel</code>.
783    * 
784    *         You may also use other model instances, e.g.,  
785    *         <code>java.util.List</code>  ,
786    *         array, and  <code>javax.faces.model.DataModel</code>.
787    *         This component will automatically convert the instance
788    *         into a <code>CollectionModel</code>.
789    * 
790    * @param value  the new value value
791    */
792   final public void setValue(Object value)
793   {
794     setProperty(VALUE_KEY, (value));
795   }
796 
797   /**
798    * Gets the maximum number of rows to display in a single range of rows.
799    * Some ranges might have fewer
800    * than the number of rows specified by this attribute (eg: the last range
801    * might have an insufficient number of rows).
802    * To display all rows at once, set this attribute to 0.
803    * The default is 25.
804    *
805    * @return  the new rows value
806    */
807   final public int getRows()
808   {
809     return ComponentUtils.resolveInteger(getProperty(ROWS_KEY), 25);
810   }
811 
812   /**
813    * Sets the maximum number of rows to display in a single range of rows.
814    * Some ranges might have fewer
815    * than the number of rows specified by this attribute (eg: the last range
816    * might have an insufficient number of rows).
817    * To display all rows at once, set this attribute to 0.
818    * The default is 25.
819    * 
820    * @param rows  the new rows value
821    */
822   final public void setRows(int rows)
823   {
824     setProperty(ROWS_KEY, Integer.valueOf(rows));
825   }
826 
827   /**
828    * Gets the index of the first row in the currently range of rows.
829    * This index is zero-based. This attribute is used to control
830    * which range of rows to display to the user.
831    *
832    * @return  the new first value
833    */
834   final public int getFirst()
835   {
836     return ComponentUtils.resolveInteger(getProperty(FIRST_KEY), 0);
837   }
838 
839   /**
840    * Sets the index of the first row in the currently range of rows.
841    * This index is zero-based. This attribute is used to control
842    * which range of rows to display to the user.
843    * 
844    * @param first  the new first value
845    */
846   final public void setFirst(int first)
847   {
848     setProperty(FIRST_KEY, Integer.valueOf(first));
849   }
850 
851   @Override
852   public String getFamily()
853   {
854     return COMPONENT_FAMILY;
855   }
856 
857   @Override
858   protected FacesBean.Type getBeanType()
859   {
860     return TYPE;
861   }
862 
863   /**
864    * Construct an instance of the UIXIterator.
865    */
866   protected UIXIterator(
867     String rendererType
868     )
869   {
870     super(rendererType);
871   }
872 
873   static
874   {
875     TYPE.lock();
876   }
877 }