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 <a href="http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a> for more. 48 * 49 * @author Thomas Spiegl (latest modification by $Author: bommel $) 50 * @version $Revision: 1187700 $ $Date: 2011-10-22 07:19:37 -0500 (Sat, 22 Oct 2011) $ 51 */ 52 public abstract class DataModel<E> implements Iterable<E> 53 { 54 private final static DataModelListener[] EMPTY_DATA_MODEL_LISTENER = new DataModelListener[]{}; 55 // FIELDS 56 private List<DataModelListener> _listeners; 57 58 private DataModelListener[] _cachedListenersArray = null; 59 60 // METHODS 61 public void addDataModelListener(DataModelListener listener) 62 { 63 if (listener == null) throw new NullPointerException("listener"); 64 if (_listeners == null) 65 { 66 _listeners = new ArrayList<DataModelListener>(); 67 } 68 _listeners.add(listener); 69 _cachedListenersArray = null; 70 } 71 72 public DataModelListener[] getDataModelListeners() 73 { 74 if (_listeners == null) 75 { 76 return EMPTY_DATA_MODEL_LISTENER; 77 } 78 if (_cachedListenersArray == null) 79 { 80 _cachedListenersArray = _listeners.toArray(new DataModelListener[_listeners.size()]); 81 } 82 return _cachedListenersArray; 83 } 84 85 /** 86 * <p> 87 * Return the number of rows of data available. 88 * </p> 89 * <p> 90 * If the number of rows of data available is not known then -1 is returned. 91 * This may happen for DataModels that wrap sources of data such as 92 * java.sql.ResultSet that provide an iterator to access the "next item" 93 * rather than a fixed-size collection of data. 94 * </p> 95 * 96 * @return the number of rows available. 97 */ 98 abstract public int getRowCount(); 99 100 /** 101 * Return the object associated with the current row index. 102 * <p> 103 * Method isRowAvailable may be called before attempting to access 104 * this method, to ensure that the data is available. 105 * 106 * @return The object associated with the current row index. 107 * @throws RuntimeException subclass of some kind if the current row index 108 * is not within the range of the current wrappedData property. 109 */ 110 abstract public E getRowData(); 111 112 /** 113 * Get the current row index. 114 * @return The current row index. 115 */ 116 abstract public int getRowIndex(); 117 118 /** 119 * Get the entire collection of data associated with this component. Note that 120 * the actual type of the returned object depends upon the concrete 121 * subclass of DataModel; the object will represent an "ordered sequence 122 * of components", but may be implemented as an array, java.util.List, 123 * java.sql.ResultSet or other similar types. 124 * 125 * @return the wrapped object. 126 */ 127 abstract public Object getWrappedData(); 128 129 /** 130 * Returns true if a call to getRowData will return a valid object. 131 * @return true if a call to getRowData will return a valid object. false otherwise. 132 */ 133 abstract public boolean isRowAvailable(); 134 135 /** 136 * {@inheritDoc} 137 * 138 * @since 2.0 139 */ 140 public Iterator<E> iterator() 141 { 142 return new DataModelIterator(); 143 } 144 145 public void removeDataModelListener(DataModelListener listener) 146 { 147 if (listener == null) throw new NullPointerException("listener"); 148 if (_listeners != null) 149 { 150 _listeners.remove(listener); 151 } 152 _cachedListenersArray = null; 153 } 154 155 /** 156 * Set the current row index. This affects the behaviour of the 157 * getRowData method in particular. 158 * 159 * @param rowIndex The row index. It may be -1 to indicate "no row", 160 * or may be a value between 0 and getRowCount()-1. 161 */ 162 abstract public void setRowIndex(int rowIndex); 163 164 /** 165 * Set the entire list of data associated with this component. Note that 166 * the actual type of the provided object must match the expectations 167 * of the concrete subclass of DataModel. See getWrappedData. 168 * 169 * @param data The object to be wrapped. 170 */ 171 abstract public void setWrappedData(Object data); 172 173 private class DataModelIterator implements Iterator<E> 174 { 175 private int nextRowIndex = 0; 176 177 public DataModelIterator() 178 { 179 setRowIndex(nextRowIndex); 180 } 181 182 public boolean hasNext() 183 { 184 //row count could be unknown, like in ResultSetDataModel 185 return isRowAvailable(); 186 } 187 188 public E next() 189 { 190 // TODO: Go to the EG with this, we need a getRowData(int) for thread safety. 191 // Or the spec needs to specify that the iterator alters the selected row explicitely 192 if (hasNext()) 193 { 194 // TODO: Double-check if this cast is safe. It should be... 195 E data = (E) getRowData(); 196 nextRowIndex++; 197 setRowIndex(nextRowIndex); 198 return data; 199 200 } 201 202 throw new NoSuchElementException("Couldn't find any element in DataModel at index " + nextRowIndex); 203 } 204 205 public void remove() 206 { 207 throw new UnsupportedOperationException(); 208 } 209 } 210 }