/******************************************************************************
* Product: Posterita Ajax UI *
* Copyright (C) 2007 Posterita Ltd. All Rights Reserved. *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* For the text or an alternative of this public license, you may reach us *
* Posterita Ltd., 3, Draper Avenue, Quatre Bornes, Mauritius *
* or via info@posterita.org or http://www.posterita.org/ *
*****************************************************************************/
package org.adempiere.webui.component;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import org.adempiere.webui.event.WTableModelEvent;
import org.adempiere.webui.event.WTableModelListener;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zul.ListModelExt;
import org.zkoss.zul.ListModelList;
import org.zkoss.zul.event.ListDataEvent;
/**
* This is a ListModel to be used with Listbox.
* The model allows for a table structure to be created, with columns
* in addition to the rows provided by {@link org.zkoss.zul.ListModelList}.
*
* @author Andrew Kimball
*
*/
public class ListModelTable extends ListModelList implements ListModelExt
{
/**
*
*/
private static final long serialVersionUID = 1891313647781142789L;
/** Array of listeners to changes in the table model. */
protected ArrayList<WTableModelListener> m_listeners = new ArrayList<WTableModelListener>();
/** The number of columns in the table. */
private int m_noColumns;
private ListModelExt sorter = null;
/**
* Default constructor.
*
*/
public ListModelTable()
{
super();
}
/**
* Construct the ListModel with a collection of objects.
* A copy is made of the collection.
* The objects should be vectors of objects
*
* @param collection The collection of objects with which to initialise the list
*/
public ListModelTable(Collection collection)
{
super(collection);
m_noColumns = 0;
for (Object row : getInnerList())
{
if (row instanceof List)
{
m_noColumns = Math.max(m_noColumns, ((List)row).size());
}
else
{
throw new IllegalArgumentException("The collection must contain list of objects");
}
}
}
/**
* Query thenumber of columns in the table.
*
* @return the number of columns in the table. 0 if uninitialised.
*/
public int getNoColumns()
{
return m_noColumns;
}
/**
* Add a column to the model.
*
*/
public void addColumn()
{
m_noColumns++;
ensureRowSize();
return;
}
/**
* Ensure that each of the rows contains the correct number of elements.
* Please note that the table cannot be shrunk.
*/
private void ensureRowSize()
{
Iterator<List<Object>> rowIterator = this.getInnerList().iterator();
while (rowIterator.hasNext())
{
List<Object> list = rowIterator.next();
if (list instanceof Vector)
((Vector<Object>)list).setSize(m_noColumns);
else
{
if (m_noColumns > list.size())
{
for(int i = list.size(); i < m_noColumns; i++)
{
list.add(null);
}
}
else if (m_noColumns < list.size())
{
for (int i = list.size(); i > m_noColumns; i--)
{
list.remove(i - 1);
}
}
}
}
}
/**
* Set the number of columns that the table is to contain.
* @param columns The number of columns.
*/
public void setNoColumns(int columns)
{
m_noColumns = columns;
ensureRowSize();
}
/**
* Query the number of rows in the table.
* @return the number of rows in the table
*/
public int getNoRows()
{
return this.getSize();
}
/**
* Returns the cell value at <code>rowIndex</code> and <code>columnIndex</code>.
*
* @param rowIndex the index of the row whose value is to be queried
* @param columnIndex the index of the column whose value is to be queried
* @return The object stored at the specified position
*/
public Object getDataAt(int rowIndex, int columnIndex)
{
List modelRow;
Object dataObject;
try
{
modelRow = (List)getElementAt(rowIndex);
dataObject = modelRow.get(columnIndex);
}
catch (Exception exception)
{
throw new IllegalArgumentException("Attempted to access "
+ "nonexistent ListModelTable field at "
+ rowIndex + ", " + columnIndex);
}
return dataObject;
}
/**
* Set the cell value at <code>row</code> and <code>column</code>.
*
* @param aValue The value to set
* @param row the index of the row whose value is to be set
* @param col the index of the column whose value is to be set
*/
public void setDataAt(Object aValue, int row, int col)
{
List<Object> vector;
WTableModelEvent tcEvent;
try
{
if (getElementAt(row) instanceof List)
{
vector = (List<Object>)getElementAt(row);
try
{
vector.set(col, aValue);
// create a new event and fire the event
tcEvent = new WTableModelEvent(this, row, col);
fireTableChange(tcEvent);
}
catch (ArrayIndexOutOfBoundsException exception)
{
throw new IllegalArgumentException("Attempted to access "
+ "nonexistent ListModelTable column at index "
+ col);
}
}
else
{
throw new IllegalArgumentException("The ListModelTable cannot contain "
+ "anything other than object vectors as its row elements");
}
}
catch (IndexOutOfBoundsException exception)
{
throw new IllegalArgumentException("Attempted to access "
+ "nonexistent ListModelTable row at index " + row);
}
return;
}
/**
* Set the number of rows in the table and initialise new rows.
* For each new row, an empty collection of the size specified by
* {@link #setNoColumns(int)} is created.
* @param rowCount The number of rows to be contained in the table
*/
public void setNoRows(int rowCount)
{
List<Object> newRow = null;
int currentSize = getSize();
if (rowCount >= currentSize)
{
boolean vector = (getInnerList() instanceof Vector) ? true : false;
while (getSize() < rowCount)
{
if (vector)
{
newRow = new Vector<Object>(getNoColumns());
((Vector)newRow).setSize(getNoColumns());
add(newRow);
}
else
{
newRow = new ArrayList<Object>(getNoColumns());
for(int i = 0; i < getNoColumns(); i++)
{
newRow.add(null);
}
add(newRow);
}
}
}
else
{
removeRange(rowCount, currentSize);
}
}
/**
* Add a listener for events from the data model.
*
* The listener will only be added if it doesn't already exist.
*
* @param listener A listener for changes in the table mode
*/
public void addTableModelListener(WTableModelListener listener)
{
if (listener == null)
{
return;
}
if (!m_listeners.contains(listener))
{
m_listeners.add(listener);
}
return;
}
public void removeTableModelListener(WTableModelListener listener)
{
m_listeners.remove(listener);
}
/**
* Send the specified <code>event</code> to all listeners.
*
* @param event The event tofire
*/
private void fireTableChange(WTableModelEvent event)
{
for (WTableModelListener listener : m_listeners)
{
listener.tableChanged(event);
}
return;
}
/*
* (non-Javadoc)
* @see org.zkoss.zul.ListModelList#sort(java.util.Comparator, boolean)
*/
public void sort(Comparator cmpr, boolean ascending)
{
if (sorter != null)
sorter.sort(cmpr, ascending);
else
Collections.sort(this.getInnerList(), cmpr);
WTableModelEvent event = new WTableModelEvent(this,
WTableModelEvent.ALL_ROWS,
WTableModelEvent.ALL_COLUMNS);
fireTableChange(event);
return;
}
/**
* alias for getDataAt, to ease porting of swing form
* @param rowIndex
* @param columnIndex
* @return column value
*/
public Object getValueAt(int rowIndex, int columnIndex) {
return getDataAt(rowIndex, columnIndex);
}
/**
* alias for setDataAt, to ease porting of swing form
* @param value
* @param row
* @param col
*/
public void setValueAt(Object value, int row, int col) {
setDataAt(value, row, col);
}
/**
* alias for getSize, to ease porting of swing form
* @return size
*/
public int getRowCount() {
return getSize();
}
/**
* Request components that attached to this model to re-render a row.
* @param row
*/
public void updateComponent(int row) {
updateComponent(row, row);
}
/**
* Request components that attached to this model to re-render a range of row.
* @param fromRow
* @param toRow
*/
public void updateComponent(int fromRow, int toRow) {
//must run from the UI thread
if (Executions.getCurrent() != null) {
fireEvent(ListDataEvent.CONTENTS_CHANGED, fromRow, toRow);
}
}
public void setSorter(ListModelExt lme)
{
sorter = lme;
}
}