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 }