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 org.apache.myfaces.component.html.ext;
20
21 import javax.faces.model.DataModel;
22 import javax.faces.model.DataModelEvent;
23 import javax.faces.model.DataModelListener;
24 import java.io.Serializable;
25 import java.util.ArrayList;
26 import java.util.List;
27
28 /**
29 * Provide a serializable equivalent of the standard DataModel classes.
30 * <p>
31 * The standard JSF UIData components accept a DataModel as the "value" for
32 * the ordered sequence of objects to be rendered in the table. Various
33 * types (List, array, ResultSet) are also accepted and automatically
34 * wrapped in one of the standard DataModel classes.
35 * <p>
36 * The standard DataModel classes are not Serializable by default, because
37 * there is no state in the class which needs to be preserved between render
38 * and postback. And the standard UIData components don't serialize the
39 * data model object, just the EL expression for the "value" attribute; the
40 * data itself is refetched when needed by re-evaluating the EL expression.
41 * <p>
42 * However there can be good reasons to serialize the list of data that is
43 * <i>wrapped</i> by the DataModel along with the UIData component. For these
44 * cases, the tomahawk t:dataTable component offers a "preserveDataModel" flag
45 * that will automatically serialize the data model along with the
46 * HtmlDataTable component; it does this by invoking the "value" binding of
47 * the t:dataTable then creating an instance of this class or one of its
48 * subclasses instead of the standard JSF DataModels.
49 * <p>
50 * This class performs two roles. It is the base implementation for specialised
51 * classes that wrap various datatypes that can be returned from the table's
52 * "value" binding. It also implements the case where the value object
53 * returned is of type DataModel.
54 * <p>
55 * When the UIData's "value" binding returns a DataModel instance, this class
56 * extracts each rowData object from the wrapped data of the original
57 * DataModel and adds these objects to an instance of this class which
58 * <i>is</i> Serializable. Of course the rowdata objects must be serializable
59 * for this to work. As a side-effect, however, the original DataModel object
60 * will be discarded, and replaced by an instance of this class. This means
61 * that any special optimisations or behaviour of the concrete DataModel
62 * subclass will be lost.
63 *
64 * @author Manfred Geiler (latest modification by $Author: grantsmith $)
65 * @version $Revision: 472630 $ $Date: 2006-11-08 15:40:03 -0500 (Wed, 08 Nov 2006) $
66 */
67 class _SerializableDataModel
68 extends DataModel
69 implements Serializable
70 {
71 private static final long serialVersionUID = -3511848078295893064L;
72 //private static final Log log = LogFactory.getLog(_SerializableDataModel.class);
73 protected int _first;
74 protected int _rows;
75 protected int _rowCount;
76 protected List _list;
77 private transient int _rowIndex = -1;
78
79 public _SerializableDataModel(int first, int rows, DataModel dataModel)
80 {
81 _first = first;
82 _rows = rows;
83 _rowCount = dataModel.getRowCount();
84 if (_rows <= 0)
85 {
86 _rows = _rowCount - first;
87 }
88 _list = new ArrayList(rows);
89 for (int i = 0; i < _rows; i++)
90 {
91 dataModel.setRowIndex(_first + i);
92 if (!dataModel.isRowAvailable()) break;
93 _list.add(dataModel.getRowData());
94 }
95 _rowIndex = -1;
96
97 DataModelListener[] dataModelListeners = dataModel.getDataModelListeners();
98 for (int i = 0; i < dataModelListeners.length; i++)
99 {
100 DataModelListener dataModelListener = dataModelListeners[i];
101 addDataModelListener(dataModelListener);
102 }
103 }
104
105 protected _SerializableDataModel()
106 {
107 }
108
109 public int getFirst()
110 {
111 return _first;
112 }
113
114 public void setFirst(int first)
115 {
116 _first = first;
117 }
118
119 public int getRows()
120 {
121 return _rows;
122 }
123
124 public void setRows(int rows)
125 {
126 _rows = rows;
127 }
128
129 public boolean isRowAvailable()
130 {
131 return _rowIndex >= _first &&
132 _rowIndex < _first + _rows &&
133 _rowIndex < _rowCount &&
134 _list.size() > _rowIndex - _first;
135 }
136
137 public int getRowCount()
138 {
139 return _rowCount;
140 }
141
142 public Object getRowData()
143 {
144 if (!isRowAvailable())
145 {
146 throw new IllegalStateException("row not available");
147 }
148 return _list.get(_rowIndex - _first);
149 }
150
151 public int getRowIndex()
152 {
153 return _rowIndex;
154 }
155
156 public void setRowIndex(int rowIndex)
157 {
158 if (rowIndex < -1)
159 {
160 throw new IllegalArgumentException();
161 }
162
163 int oldRowIndex = _rowIndex;
164 _rowIndex = rowIndex;
165 if (oldRowIndex != _rowIndex)
166 {
167 Object data = isRowAvailable() ? getRowData() : null;
168 DataModelEvent event = new DataModelEvent(this, _rowIndex, data);
169 DataModelListener[] listeners = getDataModelListeners();
170 for (int i = 0; i < listeners.length; i++)
171 {
172 listeners[i].rowSelected(event);
173 }
174 }
175 }
176
177 public Object getWrappedData()
178 {
179 return _list;
180 }
181
182 public void setWrappedData(Object obj)
183 {
184 if (obj != null)
185 {
186 throw new IllegalArgumentException("Cannot set wrapped data of _SerializableDataModel");
187 }
188 }
189
190
191
192 /*
193 // StateHolder interface
194
195 public Object saveState(FacesContext context)
196 {
197 Object values[] = new Object[4];
198 values[0] = new Integer(_first);
199 values[1] = new Integer(_rows);
200 values[2] = new Integer(_rowCount);
201 values[3] = _list;
202 return ((Object) (values));
203 }
204
205 public void restoreState(FacesContext context, Object state)
206 {
207 Object values[] = (Object[])state;
208 _first = ((Integer)values[0]).intValue();
209 _rows = ((Integer)values[1]).intValue();
210 _rowCount = ((Integer)values[2]).intValue();
211 _list = (List)values[3];
212 }
213
214 public boolean isTransient()
215 {
216 return false;
217 }
218
219 public void setTransient(boolean newTransientValue)
220 {
221 throw new UnsupportedOperationException();
222 }
223 */
224 }