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.trinidad.util;
20  
21  import java.util.AbstractMap;
22  import java.util.AbstractList;
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.Collections;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.RandomAccess;
30  import java.util.Set;
31  
32  /**
33   * <p>
34   * Bean that can dynamically produce Lists from Collections.
35   * The Collections must implement size().  Create an instance
36   * of this bean as a managed bean:
37   * </p>
38   * <h3>Example:</h3>
39   * <pre>
40   *  &lt;managed-bean&gt;
41   *    &lt;managed-bean-name&gt;makeList&lt;/managed-bean-name&gt;
42   *    &lt;managed-bean-class&gt;
43   *       org.apache.myfaces.trinidad.util.ListFromCollection
44   *    &lt;/managed-bean-class&gt;
45   *    &lt;managed-bean-scope&gt;
46   *       request
47   *    &lt;/managed-bean-scope&gt;
48   *    &lt;!-- Let's buffer 25 rows at a time (the default is 50) --&gt;
49   *    &lt;managed-property&gt;
50   *      &lt;property-name&gt;size&lt;/property-name&gt;
51   *      &lt;value&gt;25&lt;/value&gt;
52   *    &lt;managed-property&gt;
53   *  &lt;/managed-bean&gt;
54   * </pre>
55   * <pre>
56   *   &lt;h:dataTable value="#{makeList.list[someSet]}&gt;
57   *   &lt;/h:dataTable&gt;
58   * </pre>
59   * <p>
60   * Note, though, that it is extremely expensive to use this bean for
61   * the items of an tr:forEach (or c:forEach in JSF 1.2 or Facelets).
62   * </p>
63   */
64  public class ListFromCollection
65  {
66    public ListFromCollection()
67    {
68      _map = new MakeList();
69      _size = _DEFAULT_SIZE;
70    }
71  
72    public Map<Collection<?>, List<?>> getList()
73    {
74      return _map;
75    }
76  
77    public int getSize()
78    {
79      return _size;
80    }
81    
82    public void setSize(int size)
83    {
84      _size = size;
85    }
86  
87    private class MakeList extends AbstractMap<Collection<?>, List<?>>
88    {
89      @Override
90      public List<?> get(Object o)
91      {
92        if (!(o instanceof Collection))
93          return null;
94        
95        // Just send RandomAccess lists out;  wrap any other Collection
96        // into a List
97        if ((o instanceof List) &&
98            (o instanceof RandomAccess))
99          return (List) o;
100 
101       Collection<?> c = (Collection) o;
102       if (c.isEmpty())
103         return Collections.emptyList();
104 
105       return new ListImpl(c, getSize());
106     }
107 
108     @Override
109     public Set<Map.Entry<Collection<?>, List<?>>> entrySet()
110     {
111       // Not worth implementing at the moment;  this Map is only
112       // accessed from 
113       return Collections.emptySet();
114     }
115   }
116 
117   private static class ListImpl extends AbstractList<Object>
118   {
119     public ListImpl(Collection<?> c, int size)
120     {
121       _c = c;
122       _cSize = c.size();
123       if (size == 0)
124         _bufferSize = _cSize;
125       else
126         _bufferSize = Math.min(size, _cSize);
127 
128       _buffer = new ArrayList<Object>(_bufferSize);
129       _offset = -1;
130     }
131 
132     @Override
133     public int size()
134     {
135       return _cSize;
136     }
137 
138     @Override
139     public Object get(int index)
140     {
141       if ((index < 0) || (index >= _cSize))
142         throw new IndexOutOfBoundsException();
143 
144       int offset = (index / _bufferSize) * _bufferSize;
145       if (offset != _offset)
146       {
147         _loadBuffer(offset);
148         _offset = offset;
149       }
150 
151       return _buffer.get(index - _offset);
152     }
153 
154     private void _loadBuffer(int offset)
155     {
156       Iterator<? extends Object> iter = _c.iterator();
157       int i = 0;
158 
159       while (i < offset)
160       {
161         assert iter.hasNext();
162         iter.next();
163         i++;
164       }
165 
166       _buffer.clear();
167 
168       int count = 0;
169       while ((count < _bufferSize) && (i < _cSize))
170       {
171         assert iter.hasNext();
172         _buffer.add(iter.next());
173         i++;
174         count++;
175       }
176     }
177 
178     private final Collection<? extends Object> _c;
179     private final int         _bufferSize;
180     private final int         _cSize;
181     private int               _offset;
182     private ArrayList<Object> _buffer;
183   }
184 
185   private Map<Collection<?>, List<?>> _map;
186   private int                   _size;
187 
188   static private int _DEFAULT_SIZE = 50;
189 }