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 1351610 2012-06-19 08:55:52Z 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, "
164                                             + "then the 'end' attribute is required");
165         }
166     }
167 
168     public void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, FaceletException,
169             ELException
170     {
171 
172         int s = this.getBegin(ctx);
173         int e = this.getEnd(ctx);
174         int m = this.getStep(ctx);
175         Integer sO = this.begin != null ? Integer.valueOf(s) : null;
176         Integer eO = this.end != null ? Integer.valueOf(e) : null;
177         Integer mO = this.step != null ? Integer.valueOf(m) : null;
178 
179         boolean t = this.getTransient(ctx);
180         Object src = null;
181         ValueExpression srcVE = null;
182         if (this.items != null)
183         {
184             srcVE = this.items.getValueExpression(ctx, Object.class);
185             src = srcVE.getValue(ctx);
186         }
187         else
188         {
189             byte[] b = new byte[e + 1];
190             for (int i = 0; i < b.length; i++)
191             {
192                 b[i] = (byte) i;
193             }
194             src = b;
195         }
196         FaceletCompositionContext fcc = FaceletCompositionContext.getCurrentInstance(ctx);
197         if (src != null)
198         {
199             fcc.startComponentUniqueIdSection();
200             AbstractFaceletContext actx = (AbstractFaceletContext) ctx;
201             PageContext pctx = actx.getPageContext();
202             Iterator<?> itr = this.toIterator(src);
203             if (itr != null)
204             {
205                 int i = 0;
206 
207                 // move to start
208                 while (i < s && itr.hasNext())
209                 {
210                     itr.next();
211                     i++;
212                 }
213 
214                 String v = this.getVarName(ctx);
215                 String vs = this.getVarStatusName(ctx);
216                 ValueExpression ve = null;
217                 ValueExpression vO = this.capture(v, pctx);
218                 ValueExpression vsO = this.capture(vs, pctx);
219                 int mi = 0;
220                 Object value = null;
221                 try
222                 {
223                     boolean first = true;
224                     while (i <= e && itr.hasNext())
225                     {
226                         value = itr.next();
227 
228                         // set the var
229                         if (v != null)
230                         {
231                             if (t || srcVE == null)
232                             {
233                                 if (value == null)
234                                 {
235                                     pctx.getAttributes().put(v, null);
236                                 }
237                                 else
238                                 {
239                                     pctx.getAttributes().put(v, 
240                                             ctx.getExpressionFactory().createValueExpression(
241                                                 value, Object.class));
242                                 }
243                             }
244                             else
245                             {
246                                 ve = this.getVarExpr(srcVE, src, value, i);
247                                 pctx.getAttributes().put(v, ve);
248                             }
249                         }
250 
251                         // set the varStatus
252                         if (vs != null)
253                         {
254                             IterationStatus itrS = new IterationStatus(first, !itr.hasNext(), i, sO, eO, mO, value);
255                             if (t || srcVE == null)
256                             {
257                                 if (srcVE == null)
258                                 {
259                                     pctx.getAttributes().put(vs, null);
260                                 }
261                                 else
262                                 {
263                                     pctx.getAttributes().put(vs, 
264                                             ctx.getExpressionFactory().createValueExpression(
265                                                 itrS, Object.class));
266                                 }
267                             }
268                             else
269                             {
270                                 ve = new IterationStatusExpression(itrS);
271                                 pctx.getAttributes().put(vs, ve);
272                             }
273                         }
274 
275                         // execute body
276                         this.nextHandler.apply(ctx, parent);
277 
278                         // increment steps
279                         mi = 1;
280                         while (mi < m && itr.hasNext())
281                         {
282                             itr.next();
283                             mi++;
284                             i++;
285                         }
286                         i++;
287 
288                         first = false;
289                     }
290                 }
291                 finally
292                 {
293                     //Remove them from PageContext
294                     if (v != null)
295                     {
296                         pctx.getAttributes().put(v, vO);
297                     }
298                     else
299                     {
300                         pctx.getAttributes().remove(v);
301                     }
302                     if (vs != null)
303                     {
304                         pctx.getAttributes().put(vs, vsO);
305                     }
306                     else
307                     {
308                         pctx.getAttributes().remove(vs);
309                     }
310                 }
311             }
312             fcc.endComponentUniqueIdSection();
313         }
314 
315         if (fcc.isUsingPSSOnThisView() && fcc.isRefreshTransientBuildOnPSS() && !fcc.isRefreshingTransientBuild())
316         {
317             //Mark the parent component to be saved and restored fully.
318             ComponentSupport.markComponentToRestoreFully(ctx.getFacesContext(), parent);
319         }
320     }
321 
322     private final ValueExpression capture(String name, PageContext pctx)
323     {
324         if (name != null)
325         {
326             return pctx.getAttributes().put(name, null);
327         }
328         return null;
329     }
330 
331     private final int getBegin(FaceletContext ctx)
332     {
333         if (this.begin != null)
334         {
335             return this.begin.getInt(ctx);
336         }
337         return 0;
338     }
339 
340     private final int getEnd(FaceletContext ctx)
341     {
342         if (this.end != null)
343         {
344             return this.end.getInt(ctx);
345         }
346         return Integer.MAX_VALUE - 1; // hotspot bug in the JVM
347     }
348 
349     private final int getStep(FaceletContext ctx)
350     {
351         if (this.step != null)
352         {
353             return this.step.getInt(ctx);
354         }
355         return 1;
356     }
357 
358     private final boolean getTransient(FaceletContext ctx)
359     {
360         if (this.tranzient != null)
361         {
362             return this.tranzient.getBoolean(ctx);
363         }
364         return false;
365     }
366 
367     private final ValueExpression getVarExpr(ValueExpression ve, Object src, Object value, int i)
368     {
369         if (src instanceof List || src.getClass().isArray())
370         {
371             return new IndexedValueExpression(ve, i);
372         }
373         else if (src instanceof Map && value instanceof Map.Entry)
374         {
375             return new MappedValueExpression(ve, (Map.Entry) value);
376         }
377         else if (src instanceof Collection)
378         {
379             return new IteratedValueExpression(ve, value);
380         }
381         throw new IllegalStateException("Cannot create VE for: " + src);
382     }
383 
384     private final String getVarName(FaceletContext ctx)
385     {
386         if (this.var != null)
387         {
388             return this.var.getValue(ctx);
389         }
390         return null;
391     }
392 
393     private final String getVarStatusName(FaceletContext ctx)
394     {
395         if (this.varStatus != null)
396         {
397             return this.varStatus.getValue(ctx);
398         }
399         return null;
400     }
401 
402     private final Iterator<?> toIterator(Object src)
403     {
404         if (src == null)
405         {
406             return null;
407         }
408         else if (src instanceof Collection)
409         {
410             return ((Collection<?>) src).iterator();
411         }
412         else if (src instanceof Map)
413         {
414             return ((Map<?, ?>) src).entrySet().iterator();
415         }
416         else if (src.getClass().isArray())
417         {
418             return new ArrayIterator(src);
419         }
420         else
421         {
422             throw new TagAttributeException(this.tag, this.items,
423                     "Must evaluate to a Collection, Map, Array, or null.");
424         }
425     }
426 
427 }