Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.gwt.advanced.client.datamodel.EditableGridDataModel Maven / Gradle / Ivy
/*
* Copyright 2008-2012 Sergey Skladchikov
*
* Licensed 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.gwt.advanced.client.datamodel;
import com.google.gwt.core.client.GWT;
import java.util.*;
/**
* This is a model for editable grids.
* It allows dynamically add / update and remove rows and columns. It also resizes the model
* if the specified row or column is too long.
*
* @author Sergey Skladchikov
* @since 1.0.0
*/
public class EditableGridDataModel extends SimpleGridDataModel implements Editable {
/** encapsulated data */
private List data;
/** column names list */
private List columnNames = new ArrayList();
/** a list of removed rows */
private List removedRows = new ArrayList();
/** number of columns */
private int totalColumnCount;
/** data model callback handler */
private DataModelCallbackHandler handler;
/** instances of {@link EditableModelListener} */
private List listeners = new ArrayList();
/**
* Creates an instance of this class and initializes it with the data.
* It automatically resizes the data set if it's too small.
*
* @param data is a data set.
*/
public EditableGridDataModel (Object[][] data) {
super(null);
if (data == null)
data = new Object[0][0];
prepareData(data, data.length, data.length > 0 ? data[0].length : 0);
}
/**
* Creates a new instnace of this class and defines the handler.
*
* @param handler is a callback handler to be invoked on changes.
*/
@SuppressWarnings({"unchecked"})
protected EditableGridDataModel (DataModelCallbackHandler handler) {
this(new Object[0][0]);
this.handler = handler;
this.handler.synchronize(this);
}
/**
* This method adds the specified row in the data model.
* It normalizes the model if the row is too long.
*
* @param beforeRow is a number of the new row in the grid.
* @param row is new row data.
*
* @throws IllegalArgumentException if the row number is in invalid range.
*/
public void addRow(int beforeRow, Object[] row) throws IllegalArgumentException {
checkRowNumber(beforeRow, data.size() + 1);
if (row == null)
row = new Object[getTotalColumnCount()];
GridRow gridRow = normalizeColumnsCount(row);
data.add(beforeRow, gridRow);
gridRow.setIndex(beforeRow);
fireRowEvent(EditableModelEvent.ADD_ROW, beforeRow);
}
/**
* This method updates the specified row in the data model.
* It normalizes the model if the row is too long.
*
* @param rowNumber is a number of the row in the grid.
* @param row is new data.
*
* @throws IllegalArgumentException if the row number is in invalid range.
*/
public void updateRow(int rowNumber, Object[] row) throws IllegalArgumentException {
checkRowNumber(rowNumber, data.size());
if (row == null)
row = new Object[getTotalColumnCount()];
GridRow oldRow = data.get(rowNumber);
GridRow resultRow = normalizeColumnsCount(row);
resultRow.setIdentifier(oldRow.getIdentifier());
data.set(rowNumber, resultRow);
fireRowEvent(EditableModelEvent.UPDATE_ROW, rowNumber);
}
/**
* This method removes the specified row in the data model.
*
* @param rowNumber is a number of the row in the grid.
*
* @throws IllegalArgumentException if the row number is in invalid range.
*/
public void removeRow(int rowNumber) throws IllegalArgumentException {
checkRowNumber(rowNumber, data.size());
removedRows.add(data.remove(rowNumber));
fireRowEvent(EditableModelEvent.REMOVE_ROW, rowNumber);
}
/** {@inheritDoc} */
public void addColumn(int beforeColumn, Object[] column) throws IllegalArgumentException {
checkColumnNumber(beforeColumn, getTotalColumnCount());
if (column == null)
column = new Object[getTotalRowCount()];
ArrayList resultColumn = normalizeRowsCount(column);
for (int i = 0; i < resultColumn.size(); i++) {
GridRow row = data.get(i);
Object data = resultColumn.get(i);
row.add(beforeColumn, data);
}
totalColumnCount++;
fireColumnEvent(EditableModelEvent.ADD_COLUMN, beforeColumn);
}
/**
* This method adds the specified column in the data model.
* It normalizes the model if the column is too long.
*
* @param beforeColumn is a number of the new column in the grid.
* @param name is a name of the column.
* @param column is new column data.
*
* @throws IllegalArgumentException if the column number is in invalid range.
*/
public void addColumn(int beforeColumn, String name, Object[] column) throws IllegalArgumentException {
addColumn(beforeColumn, column);
getColumnNamesList().add(beforeColumn, name);
fireColumnEvent(EditableModelEvent.ADD_COLUMN, beforeColumn);
}
/**
* This method updates the specified column in the data model.
* It normalizes the model if the column is too long.
*
* @param columnNumber is a number of the column in the grid.
* @param column is new data.
*
* @throws IllegalArgumentException if the column number is in invalid range.
*/
public void updateColumn(int columnNumber, Object[] column) {
checkColumnNumber(columnNumber, getTotalColumnCount() - 1);
if (column == null)
column = new Object[getTotalRowCount()];
ArrayList resultColumn = normalizeRowsCount(column);
for (int i = 0; i < resultColumn.size(); i++) {
Object cellData = resultColumn.get(i);
data.get(i).set(columnNumber, cellData);
}
fireColumnEvent(EditableModelEvent.UPDATE_COLUMN, columnNumber);
}
/**
* This method updates the specified column in the data model.
* It normalizes the model if the column is too long.
*
* @param name is a name of the column.
* @param column is new data.
*/
public void updateColumn(String name, Object[] column) {
int index = getColumnNamesList().indexOf(name);
if (index != -1) {
updateColumn(index, column);
fireColumnEvent(EditableModelEvent.UPDATE_COLUMN, index);
}
}
/** {@inheritDoc} */
public void removeColumn(int columnNumber) throws IllegalArgumentException {
checkColumnNumber(columnNumber, getTotalColumnCount() - 1);
for (GridRow row : data)
row.remove(columnNumber);
totalColumnCount--;
fireColumnEvent(EditableModelEvent.REMOVE_COLUMN, columnNumber);
}
/**
* This method deletes the specified column in the data model.
*
* @param name is a name of the column.
*/
public void removeColumn(String name) {
int index = getColumnNamesList().indexOf(name);
if (index != -1) {
removeColumn(index);
getColumnNamesList().remove(index);
fireColumnEvent(EditableModelEvent.REMOVE_COLUMN, index);
}
}
/** {@inheritDoc} */
public Object[] getRowData (int rowNumber) {
return data.get(rowNumber).getData();
}
/** {@inheritDoc} */
public void removeAll () {
removedRows.addAll(data);
data.clear();
fireEvent(createEvent(EditableModelEvent.CLEAN));
}
/** {@inheritDoc} */
public void update (int row, int column, Object data) {
checkRowNumber(row, this.data.size());
checkColumnNumber(column, getTotalColumnCount());
this.data.get(row).set(column, data);
fireEvent(createEvent(EditableModelEvent.UPDATE_CELL, row, column));
}
/** {@inheritDoc} */
public void setSortColumn (int sortColumn, Comparator comparator) {
setSortColumn(sortColumn);
Collections.sort(data, createRowComparator(sortColumn, comparator));
int count = 0;
for (GridRow gridRow : data) {
gridRow.setIndex(count);
count++;
}
fireColumnEvent(EditableModelEvent.SORT_ALL, sortColumn);
}
/**
* This method creates a row comparator.
*
* @param sortColumn is a sort column.
* @param comparator is a cell comparator.
*
* @return is a row comparator instance.
*/
protected RowComparator createRowComparator (
int sortColumn, Comparator comparator
) {
return new RowComparator(sortColumn, comparator);
}
/**
* Getter for property 'totalColumnCount'.
*
* @return Value for property 'totalColumnCount'.
*/
public int getTotalColumnCount () {
return totalColumnCount;
}
/** {@inheritDoc} */
public Object[][] getRemovedRows() {
Object[][] rows = new Object[removedRows.size()][getTotalColumnCount()];
for (int i = 0; i < rows.length; i++) {
rows[i] = removedRows.get(i).getData();
}
return rows;
}
/** {@inheritDoc} */
public void clearRemovedRows () {
removedRows.clear();
}
/**
* This method is overriden to avoid possible recursions.
*
* @return a total row count.
*/
public int getTotalRowCount () {
return data.size();
}
/** {@inheritDoc} */
public DataModelCallbackHandler getHandler () {
return handler;
}
/** {@inheritDoc} */
public void setHandler (DataModelCallbackHandler handler) {
this.handler = handler;
}
/**
* Use this method to update the model with a new value.
*
* @param data is a data set to be applied instead of the current set.
*/
public void update(Object[][] data) {
prepareData(data, data.length, data.length > 0 ? data[0].length : 0);
fireEvent(createEvent(EditableModelEvent.UPDATE_ALL));
}
/**
* This method registers the specified listener to receive model events.
*
* @param listener is a model listener to register.
*/
public void addListener(EditableModelListener listener) {
removeListener(listener);
this.listeners.add(listener);
}
/**
* This method unregisters the specified listener to stop model events receiving.
*
* @param listener is a model listener to be removed.
*/
public void removeListener(EditableModelListener listener) {
this.listeners.remove(listener);
}
/**
* This method checks whether the specified column is in valid range and throws the
* IllegalArgumentException
if it isn't.
*
* @param columnNumber is a column number to check.
* @param max is a max limit of the range.
*
* @throws IllegalArgumentException if check failed.
*/
protected void checkColumnNumber(int columnNumber, int max) throws IllegalArgumentException {
if (columnNumber < 0 || columnNumber >= max)
throw new IllegalArgumentException("Wrong column number. It must be in range [0, " + max + "). It is " + columnNumber);
}
/**
* This method checks whether the specified row is in valid range and throws the
* IllegalArgumentException
if it isn't.
*
* @param rowNumber is a row number to check.
* @param max is a max limit of the range.
*
* @throws IllegalArgumentException if check failed.
*/
protected void checkRowNumber(int rowNumber, int max) {
if (rowNumber < 0 || rowNumber >= max)
throw new IllegalArgumentException("Wrong row number. It must be in range [0, " + max + "). It is " + rowNumber);
}
/**
* This method returns the data set of the model.
*
* @return a data set.
*/
public Object[][] getData () {
Object[][] rows = new Object[data.size()][getTotalColumnCount()];
for (int i = 0; i < rows.length; i++) {
GridRow row = data.get(i);
for (int j = 0; row != null && j < rows[i].length; j++) {
rows[i][j] = row.get(j);
}
}
return rows;
}
/**
* This method normalizes a number of columns in all rows adding empty cells.
* If the specified row is shorter then the current columns count, it add empty cells to the row.
*
* @param row is a pattern row.
*
* @return a result row.
*/
protected GridRow normalizeColumnsCount (Object[] row) {
GridRow resultRow = createGridRow(0);
resultRow.setData(row);
//normalization
if (row.length > getTotalColumnCount()) {
for (GridRow otherRow : data) {
for (int i = getTotalColumnCount(); i < row.length; i++) {
otherRow.add(null);
}
}
totalColumnCount = row.length;
} else {
for (int i = row.length; i < getTotalColumnCount(); i++) {
resultRow.add(null);
}
}
return resultRow;
}
/**
* This method normalizes a number of rows in all columns adding empty cells.
* If the specified column is shorter then the current rows count, it add empty cells to the column.
*
* @param column is a pattern column.
* @return a result column.
*/
protected ArrayList normalizeRowsCount (Object[] column) {
ArrayList resultColumn = new ArrayList(Arrays.asList(column));
//normalize
if (column.length > data.size()) {
for (int i = data.size(); i < column.length; i++) {
GridRow row = createGridRow(getTotalColumnCount());
for (int j = 0; j < getTotalColumnCount(); j++)
row.add(null);
data.add(row);
row.setIndex(i);
}
} else {
for (int i = column.length; i < getTotalRowCount(); i++) {
resultColumn.add(null);
}
}
return resultColumn;
}
/**
* This method initializes the data set with the specified values.
* It tries to fill the data set and if the specified value is shorter it adds empty cells.
* Otherwise it increases the size of the data set.
*
* @param data is a data to put into the data set.
* @param rowCount is a row count of the data set.
* @param columnCount is a column count of the data set.
*/
protected void prepareData (Object[][] data, int rowCount, int columnCount) {
rowCount = Math.max(rowCount, (data != null ? data.length : 0));
columnCount = Math.max(columnCount, (data != null && data.length > 0 ? data[0].length : 0));
this.data = new ArrayList(rowCount);
for (int i = 0; i < rowCount; i++) {
GridRow row = createGridRow(columnCount);
for (int j = 0; j < columnCount; j++) {
if (data != null && data.length > i && data[0].length > j)
row.add(data[i][j]);
else
row.add(null);
}
this.data.add(i, row);
row.setIndex(i);
}
this.totalColumnCount = columnCount;
}
/**
* This method creates a new identified list instance.
*
* @param columnCount is a column count value.
* @return a new identified list.
*/
protected GridRow createGridRow(int columnCount) {
return new GridRow(columnCount);
}
/**
* This method returns an original list of rows.
*
* @return a list of rows.
*/
public GridRow[] getRows() {
return data.toArray(new GridRow[data.size()]);
}
/** {@inheritDoc} */
public GridColumn[] getColumns() {
int columnCount = getTotalColumnCount();
GridColumn[] columns = new GridColumn[columnCount];
for (int i = 0; i < columnCount; i++)
columns[i] = getGridColumn(i);
return columns;
}
/** {@inheritDoc} */
public GridRow getRow(int index) {
return data.get(index);
}
/** {@inheritDoc} */
public GridColumn getGridColumn(int index) {
GridColumn column = new GridColumn(this);
column.setIndex(index);
if (index < getColumnNamesList().size())
column.setName(getColumnNamesList().get(index));
return column;
}
/** {@inheritDoc} */
public String[] getColumnNames() {
List columnNamesList = getColumnNamesList();
int size = columnNamesList.size();
if (size > getTotalColumnCount())
columnNamesList = getSublist(columnNamesList, 0, getTotalColumnCount());
else if (size < getTotalColumnCount()) {
columnNamesList = new ArrayList(getTotalRowCount());
columnNamesList.addAll(getColumnNamesList());
for (int i = size; i < getTotalColumnCount(); i++)
columnNamesList.add(null);
}
return columnNamesList.toArray(new String[size]);
}
/** {@inheritDoc} */
public void setColumNames(String[] names) {
getColumnNamesList().clear();
getColumnNamesList().addAll(Arrays.asList(names));
}
/**
* This method returns an internal row identifier.
*
* @param row is a row number.
* @return an identifier value.
*/
protected String getInternalRowIdentifier(int row) {
return getRows()[row].getIdentifier();
}
/**
* Getter for property 'columnNames'.
*
* @return Value for property 'columnNames'.
*/
protected List getColumnNamesList() {
return columnNames;
}
/**
* This method extract a sublist of the specified list.
*
* @param list is a list of values.
* @param start is a start index (including).
* @param end is an end index (excluding).
* @return a sublist.
*/
protected List getSublist(List list, int start, int end) {
List result = new ArrayList();
for (int i = start; i < end && i < list.size(); i++)
result.add(list.get(i));
return result;
}
/**
* Gets a list of data.
*
* @return a data list.
*/
protected List extends GridRow> getDataList() {
return data;
}
/**
* Gets a list of registered listeners.
*
* @return a list of registered listeners.
*/
protected List getListeners() {
return listeners;
}
/**
* This method prepares the specified event for sending, initilizing necessary fields.
*
* @param event is an event to be prepared.
*/
protected void prepareEvent(EditableModelEvent event) {
event.setSource(this);
}
/**
* This method fires the specified event.
*
* @param event is an event to fire.
*/
protected void fireEvent(EditableModelEvent event) {
prepareEvent(event);
for (EditableModelListener listener : getListeners()) {
try {
listener.onModelEvent(event);
} catch (Exception e) {
GWT.log(e.getMessage(), e);
}
}
}
/**
* Fires a row change events.
*
* @param eventType is a concrete row event type.
* @param row is a row number.
*/
protected void fireRowEvent(EditableModelEvent.EventType eventType, int row) {
EditableModelEvent event = createEvent(eventType);
event.setRow(row);
fireEvent(event);
}
/**
* Fires a column change events.
*
* @param eventType is a concrete column event type.
* @param column is a column number.
*/
protected void fireColumnEvent(EditableModelEvent.EventType eventType, int column) {
EditableModelEvent event = createEvent(eventType);
event.setColumn(column);
fireEvent(event);
}
/**
* Creates a new model event.
* Subclasses can override this method to support their own event classes.
*
* @param eventType is an event type.
* @return a newly constructed event.
*/
protected EditableModelEvent createEvent(EditableModelEvent.EventType eventType) {
return new EditableModelEvent(eventType);
}
/**
* Creates a new model event.
* Subclasses can override this method to support their own event classes.
*
* @param eventType is an event type.
* @param row is a row number.
* @param column is a column number.
*
* @return a newly constructed event.
*/
protected EditableModelEvent createEvent(EditableModelEvent.EventType eventType, int row, int column) {
return new EditableModelEvent(eventType, row, column);
}
/**
* This is a row comparator implementation.
*
* @author Sergey Skladchikov
*/
protected static class RowComparator implements Comparator {
/** column number */
private int column;
/** cell comparator */
private Comparator comparator;
/**
* Creates a new instance of this class.
*
* @param column is a column number.
* @param comparator is a cell comparator.
*/
public RowComparator (int column, Comparator comparator) {
this.column = column;
this.comparator = comparator;
}
/**
* Getter for property 'column'.
*
* @return Value for property 'column'.
*/
public int getColumn () {
return column;
}
/**
* Getter for property 'comparator'.
*
* @return Value for property 'comparator'.
*/
public Comparator getComparator () {
return comparator;
}
/**
* Compares cell values using the cell comparator.
*
* @param o1 is the first value.
* @param o2 is the second value.
*
* @return a result of comparison.
*/
@Override
public int compare(GridRow o1, GridRow o2) {
return comparator.compare(o1.get(column), o2.get(column));
}
}
}