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   * $Id: AbstractUILimitRendered.java 663481 2008-06-05 07:00:34Z lu4242 $
20   */
21  package org.apache.myfaces.custom.limitrendered;
22  
23  import java.io.IOException;
24  import java.lang.reflect.Array;
25  import java.util.ArrayList;
26  import java.util.Collection;
27  import java.util.Iterator;
28  import java.util.List;
29  
30  import javax.faces.component.UIComponent;
31  import javax.faces.component.UIComponentBase;
32  import javax.faces.context.FacesContext;
33  import javax.faces.el.ValueBinding;
34  
35  import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
36  
37  /**
38   * Tag that allows for selective rendering based on a count or by child index
39   * 
40   * Component that only renders a subset of its children components. Provides
41   * functionality that the JSTL choose tag has, but leverages the 
42   * {@link javax.faces.component.UIComponent#isRendered()} method instead
43   * of using when tags with test attributes.
44   * <p>It can either use a filter type of "count" or "index". If count,
45   * the given number of children will be rendered (so for example, a value of 2 will cause
46   * the first two children that are have a true <tt>isRendered</tt> result
47   * to be rendered. If index, it will render the given indexes.</p>
48   * <p>See the taglib for more documentation</p>
49   * 
50   * @JSFComponent
51   *   name = "s:limitRendered"
52   *   class = "org.apache.myfaces.custom.limitrendered.UILimitRendered"
53   *   tagClass = "org.apache.myfaces.custom.limitrendered.UILimitRenderedTag"
54   *   
55   * @author Andrew Robinson (latest modification by $Author: lu4242 $)
56   * @version $Revision: 663481 $ $Date: 2008-06-05 09:00:34 +0200 (Thu, 05 Jun 2008) $
57   */
58  public abstract class AbstractUILimitRendered extends UIComponentBase
59  {
60      public static final String COMPONENT_FAMILY = "javax.faces.Data";
61      public static final String COMPONENT_TYPE = "org.apache.myfaces.UILimitRendered";
62              
63      /**
64       * The filter type: count|index. count: the value should evaluate to a Number 
65       * or a value that can be parsed into an integer. index: A collection, 
66       * array or comma-separated list of numbers. (Default: "count")
67       * 
68       * @JSFProperty
69       * @return the type
70       */
71      public abstract String getType();
72  
73      /**
74       * @param type the type to set
75       */
76      public abstract void setType(String type);
77  
78      /**
79       *  The value valid for the type. If this evaluates to null, all children will 
80       *  be rendered. If the type is count, this value must evaluate to a java 
81       *  Number instance or a value which the toString() method can be used with 
82       *  Integer.parseInt(String). The first number of children that are rendered 
83       *  (isRendered() returns true) up to the given value will be rendered. If the 
84       *  type is index, the value must be a Collection, int[], Object[] or a 
85       *  comma-separated list of numbers. Each item in the list must be a valid 
86       *  number. If negative, it is taken from then end. If the child at the given 
87       *  index is not rendered, then that component is skipped (so the indexes are 
88       *  absolute). See the documentation on the myfaces website for more information.
89       * 
90       * @JSFProperty
91       * @return the value
92       */
93      public abstract Object getValue();
94  
95      /**
96       * @param value the value to set
97       */
98      public abstract void setValue(Object value);
99      
100     /**
101      * @see javax.faces.component.UIComponentBase#getRendersChildren()
102      */
103     public boolean getRendersChildren()
104     {
105         return true;
106     }
107     
108     /**
109      * @see javax.faces.component.UIComponentBase#encodeChildren(javax.faces.context.FacesContext)
110      */
111     public void encodeChildren(FacesContext context) throws IOException
112     {
113         if (!isRendered()) {
114             return;
115         }
116         
117         for (Iterator iter = filterChildren().iterator(); iter.hasNext();) {
118             RendererUtils.renderChild(context, (UIComponent)iter.next());
119         }
120     }
121     
122     protected List filterChildren()
123     {
124       Object value = getValue();
125       String type = getType();
126       if (type == null) {
127           type = "count";
128       }
129       
130       if ("index".equals(type)) {
131           // default to all
132           if (value == null) {
133               return getChildren();
134           }
135           return getChildrenByIndex(value);
136       } if ("count".equals(type)) {
137           return getChildrenByCount(value);
138       } else {
139           throw new IllegalArgumentException("type");
140       }
141     }
142     
143     protected List getChildrenByCount(Object value)
144     {
145         if (value == null) {
146             value = new Integer(1);
147         }
148         int count;
149         if (value instanceof Number) {
150           count = ((Number)value).intValue();
151         } else {
152           count = Integer.parseInt(value.toString());
153         }
154         
155         List children = new ArrayList(count);
156         int i = 0;
157         for (Iterator iter = getChildren().iterator(); iter.hasNext() && i < count;)
158         {
159             UIComponent child = (UIComponent)iter.next();
160             if (child.isRendered())
161             {
162                 children.add(child);
163                 ++i;
164             }
165         }
166         
167         return children;
168     }
169         
170     protected List getChildrenByIndex(Object value)
171     {
172         int[] indexes = parseIndexesValue(value);
173         
174         List retVal = new ArrayList(indexes.length);
175         List children = getChildren();
176         for (int i = 0; i < indexes.length; ++i)
177         {
178             int index = indexes[i] < 0 ? children.size() + indexes[i] :
179                 indexes[i];
180             retVal.add(children.get(index));
181         }
182         return retVal;
183     }
184     
185     protected int[] parseIndexesValue(Object value)
186     {
187         int[] indexes;
188         if (value instanceof Collection)
189         {
190             Collection c = (Collection)value;
191             indexes = new int[c.size()];
192             int i = 0;
193             for (Iterator iter = c.iterator(); iter.hasNext(); ++i)
194             {
195                 Object obj = iter.next();
196                 if (obj instanceof Number) {
197                     indexes[i] = ((Number)obj).intValue();
198                 } else {
199                     indexes[i] = Integer.parseInt(obj.toString());
200                 }
201             }
202         }
203         else if (value.getClass().isArray())
204         {
205             if (int.class.isAssignableFrom(value.getClass().getComponentType()))
206             {
207                 indexes = (int[])value;
208             }
209             else
210             {
211                 indexes = new int[Array.getLength(value)];
212                 for (int i = 0; i < indexes.length; ++i)
213                 {
214                     Object obj = Array.get(value, i);
215                     if (obj instanceof Number) {
216                         indexes[i] = ((Number)obj).intValue();
217                     } else {
218                         indexes[i] = Integer.parseInt(obj.toString());
219                     }
220                 }
221             }
222         }
223         else
224         {
225             String[] values = value.toString().split("\\s*,\\s*");
226             indexes = new int[values.length];
227             for (int i = 0; i < indexes.length; ++i) {
228                 indexes[i] = Integer.parseInt(values[i]);
229             }
230         }
231         
232         return indexes;
233     }
234 }