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 }