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.model;
20  
21  import java.beans.IntrospectionException;
22  
23  import java.util.Collections;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.NoSuchElementException;
27  
28  import javax.faces.model.ArrayDataModel;
29  import javax.faces.model.DataModel;
30  import javax.faces.model.ListDataModel;
31  import javax.faces.model.ScalarDataModel;
32  import org.apache.myfaces.trinidad.logging.TrinidadLogger;
33  
34  /**
35   * Provides convenience methods for converting objects into models, and
36   * working with models.
37   */
38  public final class ModelUtils
39  {
40  
41    /**
42     * Gets an iteration of all the rowKeys in a collection.
43     * The collection must not be modified while this iterator is being used.
44     * The Iterator is not modifiable.
45     */
46    public static Iterator<Object> getRowKeyIterator(final CollectionModel model)
47    {
48      Iterator<Object> iter = new Iterator<Object>()
49      {
50        public boolean hasNext()
51        {
52          return _next != null;
53        }
54  
55        public Object next()
56        {
57          if (_next == null)
58            throw new NoSuchElementException();
59          Object value = _next;
60          _next = _next();
61          return value;
62        }
63  
64        public void remove()
65        {
66          throw new UnsupportedOperationException();
67        }
68  
69        private Object _next()
70        {
71          int oldIndex = model.getRowIndex();
72          try
73          {
74            model.setRowIndex(_rowIndex++);
75            if (model.isRowAvailable())
76              return model.getRowKey();
77          } finally
78          {
79            model.setRowIndex(oldIndex);
80          }
81          return null;
82        }
83  
84        private Object _next = Boolean.TRUE;// bogus value used for initialization
85        private int _rowIndex = 0;
86      };
87      iter.next(); //initialize
88      return iter;
89    }
90  
91    /**
92     * finds the last index in the given RowKeyIndex that has data and returns the
93     * next index. This is useful when the {@link RowKeyIndex#getRowCount} of the
94     * RowKeyIndex is not known.
95     * @return a positive number if there is data. Returns zero if there is no data.
96     * Note that -1 is never returned.
97     */
98    public static int getRowCount(RowKeyIndex model)
99    {
100     int rowCount = model.getRowCount();
101     // is the rowCount known?
102     if (rowCount >= 0)
103     {
104       return rowCount;
105     }
106 
107     int lowerBound = 0;
108     int upperBound = 100;
109     for(model.setRowIndex(upperBound); model.isRowAvailable();)
110     {
111       lowerBound = upperBound;
112       upperBound <<= 1;
113       model.setRowIndex(upperBound);
114     }
115     return findLastIndex(model, lowerBound, upperBound);
116   }
117 
118   /**
119    * finds the last index in the given RowKeyIndex that has data and returns the
120    * next index. This is useful when the {@link RowKeyIndex#getRowCount} of the
121    * RowKeyIndex is not known.
122    * @param startIndex starts the search from this index. Use zero to start from
123    * the beginning.
124    * @param endIndex the search will stop just before this index.
125    * @return a number >= startIndex. Note that -1 is never returned.
126    */
127   public static int findLastIndex(RowKeyIndex table,
128                                   int startIndex, int endIndex)
129   {
130     int rowCount = table.getRowCount();
131     // is the rowCount known?
132     if (rowCount >= 0)
133     {
134       // rowCount is known
135       if (rowCount < endIndex)
136         endIndex = rowCount;
137 
138       // however the rowCount might have lied. see bug 4157186
139     }
140 
141     if (table.isRowAvailable(endIndex - 1))
142       return endIndex;
143 
144     final int old = table.getRowIndex();
145     try
146     {
147       while(startIndex < endIndex)
148       {
149         // TODO: issues here with overflow:
150         int middle = (startIndex + endIndex) / 2;
151         // it is possible that middle == startIndex.
152         // however, it is not possible for middle == endIndex:
153         assert (middle != endIndex) :
154           "something is grossly wrong with integer division";
155 
156         table.setRowIndex(middle);
157         if (table.isRowAvailable())
158           startIndex = middle + 1;
159         else
160           endIndex = middle;
161       }
162       return endIndex;
163     }
164     finally
165     {
166       table.setRowIndex(old);
167     }
168   }
169 
170 
171   /**
172    * Converts an instance into a TreeModel
173    */
174   public static TreeModel toTreeModel(Object value)
175   {
176     if (value instanceof TreeModel)
177       return (TreeModel) value;
178 
179     // if we don't have a treeModel at this point then
180     // try to convert the value into a list and create a
181     // tree that has no subtrees. it will just be a bunch of
182     // root nodes.
183     // Later on, if we can recognize some common POJO pattern as
184     // being a tree, we can adapt it here.
185     return new ChildPropertyTreeModel(value, null);
186   }
187 
188   /**
189    * Converts an instance into a MenuModel
190    */
191   public static MenuModel toMenuModel(Object value)
192   {
193     if (value instanceof MenuModel)
194       return (MenuModel) value;
195     else
196     {
197       try
198       {
199         return new ViewIdPropertyMenuModel(value, null);
200       }
201       catch (IntrospectionException e)
202       {
203         IllegalArgumentException re =
204           new IllegalArgumentException(_LOG.getMessage(
205             "CANNOT_CONVERT_INTO_MENUMODEL",value));
206         re.initCause(e);
207         throw re;
208       }
209     }
210   }
211 
212 
213 
214   /**
215    * Converts an instance into a CollectionModel.
216    * @param value This can be a DataModel, List, Array or other CollectionModel.
217    */
218   public static CollectionModel toCollectionModel(Object value)
219   {
220     if (value instanceof CollectionModel)
221       return (CollectionModel) value;
222     else
223     {
224       return new SortableModel(value);
225     }
226   }
227 
228   /**
229    * Converts an instance into a DataModel.
230    * @param value Supported instances include java.util.List and
231    * arrays.
232    */
233   public static DataModel toDataModel(Object value)
234   {
235     if (value instanceof DataModel)
236     {
237       return (DataModel) value;
238     }
239 
240     if (value instanceof Object[])
241     {
242       return new ArrayDataModel((Object[]) value);
243     }
244 
245     if (value == null)
246     {
247       return new ListDataModel(Collections.emptyList());
248     }
249     else if (value instanceof List)
250       return new ListDataModel((List) value);
251 
252     return new ScalarDataModel(value);
253   }
254 
255   private ModelUtils()
256   {
257   }
258   private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(
259     ModelUtils.class);
260 }