/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.myfaces.component.html.ext;
import javax.faces.model.DataModel;
import javax.faces.model.DataModelEvent;
import javax.faces.model.DataModelListener;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* Provide a serializable equivalent of the standard DataModel classes.
* <p>
* The standard JSF UIData components accept a DataModel as the "value" for
* the ordered sequence of objects to be rendered in the table. Various
* types (List, array, ResultSet) are also accepted and automatically
* wrapped in one of the standard DataModel classes.
* <p>
* The standard DataModel classes are not Serializable by default, because
* there is no state in the class which needs to be preserved between render
* and postback. And the standard UIData components don't serialize the
* data model object, just the EL expression for the "value" attribute; the
* data itself is refetched when needed by re-evaluating the EL expression.
* <p>
* However there can be good reasons to serialize the list of data that is
* <i>wrapped</i> by the DataModel along with the UIData component. For these
* cases, the tomahawk t:dataTable component offers a "preserveDataModel" flag
* that will automatically serialize the data model along with the
* HtmlDataTable component; it does this by invoking the "value" binding of
* the t:dataTable then creating an instance of this class or one of its
* subclasses instead of the standard JSF DataModels.
* <p>
* This class performs two roles. It is the base implementation for specialised
* classes that wrap various datatypes that can be returned from the table's
* "value" binding. It also implements the case where the value object
* returned is of type DataModel.
* <p>
* When the UIData's "value" binding returns a DataModel instance, this class
* extracts each rowData object from the wrapped data of the original
* DataModel and adds these objects to an instance of this class which
* <i>is</i> Serializable. Of course the rowdata objects must be serializable
* for this to work. As a side-effect, however, the original DataModel object
* will be discarded, and replaced by an instance of this class. This means
* that any special optimisations or behaviour of the concrete DataModel
* subclass will be lost.
*
* @author Manfred Geiler (latest modification by $Author: grantsmith $)
* @version $Revision: 472630 $ $Date: 2006-11-08 21:40:03 +0100 (Mi, 08 Nov 2006) $
*/
class _SerializableDataModel
extends DataModel
implements Serializable
{
private static final long serialVersionUID = -3511848078295893064L;
//private static final Log log = LogFactory.getLog(_SerializableDataModel.class);
protected int _first;
protected int _rows;
protected int _rowCount;
protected List _list;
private transient int _rowIndex = -1;
public _SerializableDataModel(int first, int rows, DataModel dataModel)
{
_first = first;
_rows = rows;
_rowCount = dataModel.getRowCount();
if (_rows <= 0)
{
_rows = _rowCount - first;
}
_list = new ArrayList(rows);
for (int i = 0; i < _rows; i++)
{
dataModel.setRowIndex(_first + i);
if (!dataModel.isRowAvailable()) break;
_list.add(dataModel.getRowData());
}
_rowIndex = -1;
DataModelListener[] dataModelListeners = dataModel.getDataModelListeners();
for (int i = 0; i < dataModelListeners.length; i++)
{
DataModelListener dataModelListener = dataModelListeners[i];
addDataModelListener(dataModelListener);
}
}
protected _SerializableDataModel()
{
}
public int getFirst()
{
return _first;
}
public void setFirst(int first)
{
_first = first;
}
public int getRows()
{
return _rows;
}
public void setRows(int rows)
{
_rows = rows;
}
public boolean isRowAvailable()
{
return _rowIndex >= _first &&
_rowIndex < _first + _rows &&
_rowIndex < _rowCount &&
_list.size() > _rowIndex - _first;
}
public int getRowCount()
{
return _rowCount;
}
public Object getRowData()
{
if (!isRowAvailable())
{
throw new IllegalStateException("row not available");
}
return _list.get(_rowIndex - _first);
}
public int getRowIndex()
{
return _rowIndex;
}
public void setRowIndex(int rowIndex)
{
if (rowIndex < -1)
{
throw new IllegalArgumentException();
}
int oldRowIndex = _rowIndex;
_rowIndex = rowIndex;
if (oldRowIndex != _rowIndex)
{
Object data = isRowAvailable() ? getRowData() : null;
DataModelEvent event = new DataModelEvent(this, _rowIndex, data);
DataModelListener[] listeners = getDataModelListeners();
for (int i = 0; i < listeners.length; i++)
{
listeners[i].rowSelected(event);
}
}
}
public Object getWrappedData()
{
return _list;
}
public void setWrappedData(Object obj)
{
if (obj != null)
{
throw new IllegalArgumentException("Cannot set wrapped data of _SerializableDataModel");
}
}
/*
// StateHolder interface
public Object saveState(FacesContext context)
{
Object values[] = new Object[4];
values[0] = new Integer(_first);
values[1] = new Integer(_rows);
values[2] = new Integer(_rowCount);
values[3] = _list;
return ((Object) (values));
}
public void restoreState(FacesContext context, Object state)
{
Object values[] = (Object[])state;
_first = ((Integer)values[0]).intValue();
_rows = ((Integer)values[1]).intValue();
_rowCount = ((Integer)values[2]).intValue();
_list = (List)values[3];
}
public boolean isTransient()
{
return false;
}
public void setTransient(boolean newTransientValue)
{
throw new UnsupportedOperationException();
}
*/
}