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