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 }