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