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 javax.faces.model;
20  
21  import java.util.ArrayList;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.NoSuchElementException;
25  
26  /**
27    * Represents the data presented by a UIData component, together with
28    * some state information about the currently selected row within the
29    * datalist for use by listeners on UIData components. This class allows
30    * managed bean code to avoid binding directly to UIData components for
31    * typical uses.
32    * <p> 
33    * Note that DataModel and its standard subclasses are not serializable,
34    * as there is no state in a DataModel object itself that needs to be
35    * preserved between render and restore-view. UIData components therefore
36    * do not store their DataModel when serialized; they just evaluate their
37    * "value" EL expression to refetch the object during the 
38    * apply-request-values phase.
39    * <p>
40    * Because DataModel is not serializable, any managed bean that needs to
41    * be serialized and which has a member of type DataModel should therefore
42    * mark that member transient. If there is a need to preserve the datalist
43    * contained within the DataModel then ensure a reference to that list is
44    * stored in a non-transient member, or use a custom serialization method
45    * that explicitly serializes dataModel.getWrappedData.
46    *  
47    * See Javadoc of
48   * <a href="http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a> for more.
49    * 
50    * @author Thomas Spiegl (latest modification by $Author: struberg $)
51    * @version $Revision: 1188598 $ $Date: 2011-10-25 05:46:21 -0500 (Tue, 25 Oct 2011) $
52  */
53  public abstract class DataModel<E> implements Iterable<E>
54  {
55      private final static DataModelListener[] EMPTY_DATA_MODEL_LISTENER = new DataModelListener[]{};
56      // FIELDS
57      private List<DataModelListener> _listeners;
58      
59      private DataModelListener[] _cachedListenersArray = null;
60  
61      // METHODS
62      public void addDataModelListener(DataModelListener listener)
63      {
64          if (listener == null)
65          {
66              throw new NullPointerException("listener");
67          }
68          if (_listeners == null)
69          {
70              _listeners = new ArrayList<DataModelListener>();
71          }
72          _listeners.add(listener);
73          _cachedListenersArray = null;
74      }
75  
76      public DataModelListener[] getDataModelListeners()
77      {
78          if (_listeners == null)
79          {
80              return EMPTY_DATA_MODEL_LISTENER;
81          }
82          if (_cachedListenersArray == null)
83          {
84              _cachedListenersArray = _listeners.toArray(new DataModelListener[_listeners.size()]);
85          }
86          return _cachedListenersArray;
87      }
88  
89      /**
90       * <p>
91       * Return the number of rows of data available. 
92       * </p>
93       * <p>
94       * If the number of rows of data available is not known then -1 is returned.
95       * This may happen for DataModels that wrap sources of data such as 
96       * java.sql.ResultSet that provide an iterator to access the "next item"
97       * rather than a fixed-size collection of data.
98       * </p>
99       *
100      * @return the number of rows available.
101      */
102     abstract public int getRowCount();
103 
104     /**
105      * Return the object associated with the current row index.
106      * <p>
107      * Method isRowAvailable may be called before attempting to access
108      * this method, to ensure that the data is available.
109      *
110      * @return The object associated with the current row index.
111      * @throws RuntimeException subclass of some kind if the current row index
112      * is not within the range of the current wrappedData property.
113      */
114     abstract public E getRowData();
115 
116     /**
117      * Get the current row index.
118      * @return The current row index.
119      */
120     abstract public int getRowIndex();
121 
122     /**
123      * Get the entire collection of data associated with this component. Note that
124      * the actual type of the returned object depends upon the concrete
125      * subclass of DataModel; the object will represent an "ordered sequence
126      * of components", but may be implemented as an array, java.util.List,
127      * java.sql.ResultSet or other similar types.
128      *
129      * @return the wrapped object.
130      */
131     abstract public Object getWrappedData();
132 
133     /**
134      * Returns true if a call to getRowData will return a valid object.
135      * @return true if a call to getRowData will return a valid object. false otherwise.
136      */
137     abstract public boolean isRowAvailable();
138     
139     /**
140      * {@inheritDoc}
141      * 
142      * @since 2.0
143      */
144     public Iterator<E> iterator()
145     {
146         return new DataModelIterator();
147     }
148 
149     public void removeDataModelListener(DataModelListener listener)
150     {
151         if (listener == null)
152         {
153             throw new NullPointerException("listener");
154         }
155         if (_listeners != null)
156         {
157             _listeners.remove(listener);
158         }
159         _cachedListenersArray = null;
160     }
161 
162     /**
163      * Set the current row index. This affects the behaviour of the
164      * getRowData method in particular.
165      * 
166      * @param rowIndex The row index. It may be -1 to indicate "no row",
167      *                 or may be a value between 0 and getRowCount()-1. 
168      */
169     abstract public void setRowIndex(int rowIndex);
170 
171     /**
172      * Set the entire list of data associated with this component. Note that
173      * the actual type of the provided object must match the expectations
174      * of the concrete subclass of DataModel. See getWrappedData.
175      *
176      * @param data The object to be wrapped.
177      */
178     abstract public void setWrappedData(Object data);
179     
180     private class DataModelIterator implements Iterator<E>
181     {
182         private int nextRowIndex = 0;
183         
184         public DataModelIterator()
185         {
186             setRowIndex(nextRowIndex);
187         }
188         
189         public boolean hasNext()
190         {
191             //row count could be unknown, like in ResultSetDataModel
192             return isRowAvailable();
193         }
194 
195         public E next()
196         {
197             // TODO: Go to the EG with this, we need a getRowData(int) for thread safety.
198             //       Or the spec needs to specify that the iterator alters the selected row explicitely
199             if (hasNext())
200             {
201                 // TODO: Double-check if this cast is safe. It should be...
202                 E data = (E) getRowData();
203                 nextRowIndex++;
204                 setRowIndex(nextRowIndex);
205                 return data; 
206                 
207             }
208             
209             throw new NoSuchElementException("Couldn't find any element in DataModel at index " + nextRowIndex);
210         }
211 
212         public void remove()
213         {
214             throw new UnsupportedOperationException();
215         }
216     }
217 }