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.tag.jstl.core;
20  
21  import java.io.IOException;
22  import java.lang.reflect.Array;
23  import java.util.Collection;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  
28  import javax.el.ELException;
29  import javax.el.ValueExpression;
30  import javax.faces.FacesException;
31  import javax.faces.component.UIComponent;
32  import javax.faces.view.facelets.FaceletContext;
33  import javax.faces.view.facelets.FaceletException;
34  import javax.faces.view.facelets.TagAttribute;
35  import javax.faces.view.facelets.TagAttributeException;
36  import javax.faces.view.facelets.TagConfig;
37  import javax.faces.view.facelets.TagHandler;
38  
39  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFFaceletAttribute;
40  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFFaceletTag;
41  import org.apache.myfaces.view.facelets.AbstractFaceletContext;
42  import org.apache.myfaces.view.facelets.FaceletCompositionContext;
43  import org.apache.myfaces.view.facelets.PageContext;
44  import org.apache.myfaces.view.facelets.tag.ComponentContainerHandler;
45  import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport;
46  
47  /**
48   * The basic iteration tag, accepting many different
49   * collection types and supporting subsetting and other
50   * functionality
51   * 
52   * @author Jacob Hookom
53   * @author Andrew Robinson
54   * @version $Id: ForEachHandler.java 1351609 2012-06-19 08:54:37Z lu4242 $
55   */
56  @JSFFaceletTag(name="c:forEach")
57  public final class ForEachHandler extends TagHandler implements ComponentContainerHandler
58  {
59  
60      private static class ArrayIterator implements Iterator<Object>
61      {
62  
63          protected final Object array;
64  
65          protected int i;
66  
67          protected final int len;
68  
69          public ArrayIterator(Object src)
70          {
71              this.i = 0;
72              this.array = src;
73              this.len = Array.getLength(src);
74          }
75  
76          public boolean hasNext()
77          {
78              return this.i < this.len;
79          }
80  
81          public Object next()
82          {
83              return Array.get(this.array, this.i++);
84          }
85  
86          public void remove()
87          {
88              throw new UnsupportedOperationException();
89          }
90      }
91  
92      /**
93       * If items specified:
94       * Iteration begins at the item located at the
95       * specified index. First item of the collection has
96       * index 0.
97       * If items not specified:
98       * Iteration begins with index set at the value
99       * specified.
100      */
101     @JSFFaceletAttribute(className="int")
102     private final TagAttribute begin;
103 
104     /**
105      * If items specified:
106      * Iteration ends at the item located at the
107      * specified index (inclusive).
108      * If items not specified:
109      * Iteration ends when index reaches the value
110      * specified.
111      */
112     @JSFFaceletAttribute(className="int")
113     private final TagAttribute end;
114 
115     /**
116      * Collection of items to iterate over.
117      */
118     @JSFFaceletAttribute(className="javax.el.ValueExpression")
119     private final TagAttribute items;
120 
121     /**
122      * Iteration will only process every step items of
123      * the collection, starting with the first one.
124      */
125     @JSFFaceletAttribute(className="int")
126     private final TagAttribute step;
127 
128     private final TagAttribute tranzient;
129 
130     /**
131      * Name of the exported scoped variable for the
132      * current item of the iteration. This scoped
133      * variable has nested visibility. Its type depends
134      * on the object of the underlying collection.
135      */
136     @JSFFaceletAttribute(className="java.lang.String")
137     private final TagAttribute var;
138 
139     /**
140      * Name of the exported scoped variable for the
141      * status of the iteration. 
142      */
143     @JSFFaceletAttribute(className="java.lang.String")
144     private final TagAttribute varStatus;
145 
146     /**
147      * @param config
148      */
149     public ForEachHandler(TagConfig config)
150     {
151         super(config);
152         this.items = this.getAttribute("items");
153         this.var = this.getAttribute("var");
154         this.begin = this.getAttribute("begin");
155         this.end = this.getAttribute("end");
156         this.step = this.getAttribute("step");
157         this.varStatus = this.getAttribute("varStatus");
158         this.tranzient = this.getAttribute("transient");
159 
160         if (this.items == null && this.begin != null && this.end == null)
161         {
162             throw new TagAttributeException(this.tag, this.begin,
163                                             "If the 'items' attribute is not specified, but the 'begin' attribute is, then the 'end' attribute is required");
164         }
165     }
166 
167     public void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, FaceletException,
168             ELException
169     {
170 
171         int s = this.getBegin(ctx);
172         int e = this.getEnd(ctx);
173         int m = this.getStep(ctx);
174         Integer sO = this.begin != null ? Integer.valueOf(s) : null;
175         Integer eO = this.end != null ? Integer.valueOf(e) : null;
176         Integer mO = this.step != null ? Integer.valueOf(m) : null;
177 
178         boolean t = this.getTransient(ctx);
179         Object src = null;
180         ValueExpression srcVE = null;
181         if (this.items != null)
182         {
183             srcVE = this.items.getValueExpression(ctx, Object.class);
184             src = srcVE.getValue(ctx);
185         }
186         else
187         {
188             byte[] b = new byte[e + 1];
189             for (int i = 0; i < b.length; i++)
190             {
191                 b[i] = (byte) i;
192             }
193             src = b;
194         }
195         FaceletCompositionContext fcc = FaceletCompositionContext.getCurrentInstance(ctx);
196         if (src != null)
197         {
198             fcc.startComponentUniqueIdSection();
199             AbstractFaceletContext actx = (AbstractFaceletContext) ctx;
200             PageContext pctx = actx.getPageContext();
201             Iterator<?> itr = this.toIterator(src);
202             if (itr != null)
203             {
204                 int i = 0;
205 
206                 // move to start
207                 while (i < s && itr.hasNext())
208                 {
209                     itr.next();
210                     i++;
211                 }
212 
213                 String v = this.getVarName(ctx);
214                 String vs = this.getVarStatusName(ctx);
215                 ValueExpression ve = null;
216                 ValueExpression vO = this.capture(v, pctx);
217                 ValueExpression vsO = this.capture(vs, pctx);
218                 int mi = 0;
219                 Object value = null;
220                 try
221                 {
222                     boolean first = true;
223                     while (i <= e && itr.hasNext())
224                     {
225                         value = itr.next();
226 
227                         // set the var
228                         if (v != null)
229                         {
230                             if (t || srcVE == null)
231                             {
232                                 if (value == null)
233                                 {
234                                     pctx.getAttributes().put(v, null);
235                                 }
236                                 else
237                                 {
238                                     pctx.getAttributes().put(v, 
239                                             ctx.getExpressionFactory().createValueExpression(
240                                                 value, Object.class));
241                                 }
242                             }
243                             else
244                             {
245                                 ve = this.getVarExpr(srcVE, src, value, i);
246                                 pctx.getAttributes().put(v, ve);
247                             }
248                         }
249 
250                         // set the varStatus
251                         if (vs != null)
252                         {
253                             IterationStatus itrS = new IterationStatus(first, !itr.hasNext(), i, sO, eO, mO, value);
254                             if (t || srcVE == null)
255                             {
256                                 if (srcVE == null)
257                                 {
258                                     pctx.getAttributes().put(vs, null);
259                                 }
260                                 else
261                                 {
262                                     pctx.getAttributes().put(vs, 
263                                             ctx.getExpressionFactory().createValueExpression(
264                                                 itrS, Object.class));
265                                 }
266                             }
267                             else
268                             {
269                                 ve = new IterationStatusExpression(itrS);
270                                 pctx.getAttributes().put(vs, ve);
271                             }
272                         }
273 
274                         // execute body
275                         this.nextHandler.apply(ctx, parent);
276 
277                         // increment steps
278                         mi = 1;
279                         while (mi < m && itr.hasNext())
280                         {
281                             itr.next();
282                             mi++;
283                             i++;
284                         }
285                         i++;
286 
287                         first = false;
288                     }
289                 }
290                 finally
291                 {
292                     //Remove them from PageContext
293                     if (v != null)
294                     {
295                         pctx.getAttributes().put(v, vO);
296                     }
297                     else
298                     {
299                         pctx.getAttributes().remove(v);
300                     }
301                     if (vs != null)
302                     {
303                         pctx.getAttributes().put(vs, vsO);
304                     }
305                     else
306                     {
307                         pctx.getAttributes().remove(vs);
308                     }
309                 }
310             }
311             fcc.endComponentUniqueIdSection();
312         }
313 
314         if (fcc.isUsingPSSOnThisView() && fcc.isRefreshTransientBuildOnPSS() && !fcc.isRefreshingTransientBuild())
315         {
316             //Mark the parent component to be saved and restored fully.
317             ComponentSupport.markComponentToRestoreFully(ctx.getFacesContext(), parent);
318         }
319     }
320 
321     private final ValueExpression capture(String name, PageContext pctx)
322     {
323         if (name != null)
324         {
325             return pctx.getAttributes().put(name, null);
326         }
327         return null;
328     }
329 
330     private final int getBegin(FaceletContext ctx)
331     {
332         if (this.begin != null)
333         {
334             return this.begin.getInt(ctx);
335         }
336         return 0;
337     }
338 
339     private final int getEnd(FaceletContext ctx)
340     {
341         if (this.end != null)
342         {
343             return this.end.getInt(ctx);
344         }
345         return Integer.MAX_VALUE - 1; // hotspot bug in the JVM
346     }
347 
348     private final int getStep(FaceletContext ctx)
349     {
350         if (this.step != null)
351         {
352             return this.step.getInt(ctx);
353         }
354         return 1;
355     }
356 
357     private final boolean getTransient(FaceletContext ctx)
358     {
359         if (this.tranzient != null)
360         {
361             return this.tranzient.getBoolean(ctx);
362         }
363         return false;
364     }
365 
366     private final ValueExpression getVarExpr(ValueExpression ve, Object src, Object value, int i)
367     {
368         if (src instanceof List || src.getClass().isArray())
369         {
370             return new IndexedValueExpression(ve, i);
371         }
372         else if (src instanceof Map && value instanceof Map.Entry)
373         {
374             return new MappedValueExpression(ve, (Map.Entry) value);
375         }
376         else if (src instanceof Collection)
377         {
378             return new IteratedValueExpression(ve, value);
379         }
380         throw new IllegalStateException("Cannot create VE for: " + src);
381     }
382 
383     private final String getVarName(FaceletContext ctx)
384     {
385         if (this.var != null)
386         {
387             return this.var.getValue(ctx);
388         }
389         return null;
390     }
391 
392     private final String getVarStatusName(FaceletContext ctx)
393     {
394         if (this.varStatus != null)
395         {
396             return this.varStatus.getValue(ctx);
397         }
398         return null;
399     }
400 
401     private final Iterator<?> toIterator(Object src)
402     {
403         if (src == null)
404         {
405             return null;
406         }
407         else if (src instanceof Collection)
408         {
409             return ((Collection<?>) src).iterator();
410         }
411         else if (src instanceof Map)
412         {
413             return ((Map<?, ?>) src).entrySet().iterator();
414         }
415         else if (src.getClass().isArray())
416         {
417             return new ArrayIterator(src);
418         }
419         else
420         {
421             throw new TagAttributeException(this.tag, this.items, "Must evaluate to a Collection, Map, Array, or null.");
422         }
423     }
424 
425 }