/*
* Copyright (c) 2011, Nikolaus Moll
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the jo-widgets.org nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL jo-widgets.org BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.jowidgets.impl.widgets.basic;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.jowidgets.api.model.table.ITableColumn;
import org.jowidgets.api.model.table.ITableColumnModel;
import org.jowidgets.common.model.ITableCell;
import org.jowidgets.common.model.ITableColumnModelListener;
import org.jowidgets.common.model.ITableColumnModelObservable;
import org.jowidgets.common.model.ITableColumnModelSpi;
import org.jowidgets.common.model.ITableDataModel;
import org.jowidgets.common.model.ITableDataModelObservable;
import org.jowidgets.spi.widgets.ITableSpi;
import org.jowidgets.tools.controller.TableColumnModelObservable;
import org.jowidgets.util.Assert;
public class TableModelSpiAdapter implements ITableColumnModelSpi, ITableDataModel {
private final ITableColumnModel columnModel;
private final ITableDataModel dataModel;
private final TableColumnModelObservable columnModelObservable;
private ITableSpi table;
private int[] modelToView;
private int[] viewToModel; // visible List
private final ArrayList<Integer> currentColumnPermutation;
private boolean ignoreTablePermutationEvents;
public TableModelSpiAdapter(final ITableColumnModel columnModel, final ITableDataModel dataModel) {
Assert.paramNotNull(columnModel, "columnModel");
Assert.paramNotNull(dataModel, "dataModel");
this.columnModel = columnModel;
this.dataModel = dataModel;
this.ignoreTablePermutationEvents = false;
this.currentColumnPermutation = new ArrayList<Integer>(columnModel.getColumnCount());
for (int i = 0; i < columnModel.getColumnCount(); i++) {
currentColumnPermutation.add(Integer.valueOf(i));
}
this.columnModelObservable = new TableColumnModelObservable();
modelToView = new int[columnModel.getColumnCount()];
updateMappings();
for (int modelIndex = 0; modelIndex < columnModel.getColumnCount(); modelIndex++) {
final ITableColumn column = columnModel.getColumn(modelIndex);
if (!column.isVisible() && modelToView[modelIndex] >= 0) {
final int index = hideColumn(modelIndex);
columnModelObservable.fireColumnsRemoved(new int[] {index});
}
}
// Delegate events from app model to spi model
final ITableColumnModelObservable tableColumnModelObservable = columnModel.getTableColumnModelObservable();
if (tableColumnModelObservable != null) {
tableColumnModelObservable.addColumnModelListener(new ITableColumnModelListener() {
@Override
public void columnsRemoved(final int[] columnIndices) {
table.stopEditing();
final List<Integer> sortedIndices = getSortedList(columnIndices);
for (final int modelIndex : sortedIndices) {
final int removedIndex = modelToView[modelIndex];
removeColumnFromModel(modelIndex);
updateMappings();
ignoreTablePermutationEvents = true;
columnModelObservable.fireColumnsRemoved(new int[] {removedIndex});
if (table != null) {
table.setColumnPermutation(createTableColumnPermutation());
}
ignoreTablePermutationEvents = false;
}
}
@Override
public void columnsChanged(final int[] columnIndices) {
table.stopEditing();
final List<Integer> sortedIndices = getSortedList(columnIndices);
for (final int modelIndex : sortedIndices) {
final ITableColumn column = columnModel.getColumn(modelIndex);
if (column.isVisible()) {
if (modelToView[modelIndex] < 0) {
final int index = showColumn(modelIndex);
ignoreTablePermutationEvents = true;
columnModelObservable.fireColumnsAdded(new int[] {index});
if (table != null) {
table.setColumnPermutation(createTableColumnPermutation());
}
ignoreTablePermutationEvents = false;
}
else {
columnModelObservable.fireColumnsChanged(new int[] {modelToView[modelIndex]});
}
}
else if (modelToView[modelIndex] >= 0) {
final int index = hideColumn(modelIndex);
columnModelObservable.fireColumnsRemoved(new int[] {index});
}
}
}
@Override
public void columnsAdded(final int[] columnIndices) {
table.stopEditing();
final List<Integer> sortedIndices = getSortedList(columnIndices);
for (final int modelIndex : sortedIndices) {
final ITableColumn column = columnModel.getColumn(modelIndex);
insertColumnToModel(modelIndex, column.isVisible() ? 1 : -1);
updateMappings();
if (column.isVisible()) {
ignoreTablePermutationEvents = true;
columnModelObservable.fireColumnsAdded(new int[] {modelToView[modelIndex]});
if (table != null) {
table.setColumnPermutation(createTableColumnPermutation());
}
ignoreTablePermutationEvents = false;
}
}
}
});
}
}
public void setTable(final ITableSpi table) {
Assert.paramNotNull(table, "table");
this.table = table;
}
private void updateMappings() {
final ArrayList<Integer> visibleColumns = new ArrayList<Integer>();
for (int i = 0; i < modelToView.length; i++) {
if (modelToView[i] >= 0) {
modelToView[i] = visibleColumns.size();
visibleColumns.add(i);
}
else {
modelToView[i] = -1;
}
}
viewToModel = new int[visibleColumns.size()];
for (int i = 0; i < viewToModel.length; i++) {
viewToModel[i] = visibleColumns.get(i);
}
}
private int showColumn(final int columnIndex) {
if (modelToView[columnIndex] >= 0) {
throw new IllegalStateException("Column is already visible (" + columnIndex + ").");
}
modelToView[columnIndex] = 1;
updateMappings();
int viewIndex = 0;
for (int i = 0; i < columnIndex; i++) {
if (modelToView[i] >= 0) {
viewIndex++;
}
}
return viewIndex;
}
private int hideColumn(final int columnIndex) {
if (modelToView[columnIndex] < 0) {
throw new IllegalStateException("Column is already hidden (" + columnIndex + ").");
}
final int result = modelToView[columnIndex];
modelToView[columnIndex] = -1;
updateMappings();
return result;
}
@Override
public int getColumnCount() {
return viewToModel.length;
}
@Override
public ITableColumn getColumn(final int columnIndex) {
return columnModel.getColumn(viewToModel[columnIndex]);
}
@Override
public ITableColumnModelObservable getTableColumnModelObservable() {
return columnModelObservable;
}
@Override
public int getRowCount() {
return dataModel.getRowCount();
}
@Override
public ITableCell getCell(final int rowIndex, final int columnIndex) {
return dataModel.getCell(rowIndex, viewToModel[columnIndex]);
}
@Override
public ArrayList<Integer> getSelection() {
return dataModel.getSelection();
}
@Override
public void setSelection(final Collection<Integer> selection) {
dataModel.setSelection(selection);
}
@Override
public ITableDataModelObservable getTableDataModelObservable() {
return dataModel.getTableDataModelObservable();
}
public int convertViewToModel(final int columnIndex) {
if (columnIndex != -1) {
return viewToModel[columnIndex];
}
else {
return columnIndex;
}
}
public int convertModelToView(final int columnIndex) {
if (columnIndex != -1) {
return modelToView[columnIndex];
}
else {
return columnIndex;
}
}
private ArrayList<Integer> createTableColumnPermutation() {
final ArrayList<Integer> result = new ArrayList<Integer>(viewToModel.length);
for (int i = 0; i < currentColumnPermutation.size(); i++) {
final int modelIndex = currentColumnPermutation.get(i);
if (modelToView[modelIndex] >= 0) {
result.add(Integer.valueOf(modelToView[modelIndex]));
}
}
return result;
}
List<Integer> convertColumnPermutationToView(final List<Integer> permutation) {
final ArrayList<Integer> result = new ArrayList<Integer>();
for (final Integer modelIndex : permutation) {
if (modelToView[modelIndex] >= 0) {
result.add(Integer.valueOf(modelToView[modelIndex]));
}
}
return result;
}
private int getNextVisibleIndex(int position) {
while (position < currentColumnPermutation.size()) {
final int currentIndex = currentColumnPermutation.get(position);
if (modelToView[currentIndex] >= 0) {
return position;
}
position++;
}
return -1;
}
void tableColumnPermutationChanged(final ArrayList<Integer> tablePermutation) {
if (ignoreTablePermutationEvents) {
return;
}
int position = 0;
for (final int tableColumnIndex : tablePermutation) {
final int modelColumnIndex = viewToModel[tableColumnIndex];
final int nextVisible = getNextVisibleIndex(position);
if (currentColumnPermutation.get(nextVisible) == modelColumnIndex) {
position = nextVisible + 1;
continue;
}
currentColumnPermutation.remove(Integer.valueOf(modelColumnIndex));
currentColumnPermutation.add(position, Integer.valueOf(modelColumnIndex));
position++;
}
}
ArrayList<Integer> getCurrentPermutation() {
return currentColumnPermutation;
}
private void insertColumnToModel(final int index, final int value) {
final int permutationIndex = currentColumnPermutation.indexOf(Math.max(0, index - 1)) + 1;
for (int i = 0; i < currentColumnPermutation.size(); i++) {
final int permutationValue = currentColumnPermutation.get(i);
if (permutationValue >= index) {
currentColumnPermutation.set(i, Integer.valueOf(permutationValue + 1));
}
}
currentColumnPermutation.add(permutationIndex, Integer.valueOf(index));
final int[] newModelToView = new int[modelToView.length + 1];
for (int i = 0; i < index; i++) {
newModelToView[i] = modelToView[i];
}
newModelToView[index] = value;
for (int i = index; i < modelToView.length; i++) {
newModelToView[i + 1] = modelToView[i];
}
modelToView = newModelToView;
}
private void removeColumnFromModel(final int index) {
currentColumnPermutation.remove(Integer.valueOf(index));
for (int i = 0; i < currentColumnPermutation.size(); i++) {
final int permutationValue = currentColumnPermutation.get(i);
if (permutationValue >= index) {
currentColumnPermutation.set(i, Integer.valueOf(permutationValue - 1));
}
}
final int[] newModelToView = new int[modelToView.length - 1];
for (int i = 0; i < index; i++) {
newModelToView[i] = modelToView[i];
}
for (int i = index + 1; i < modelToView.length; i++) {
newModelToView[i - 1] = modelToView[i];
}
modelToView = newModelToView;
}
private List<Integer> getSortedList(final int[] array) {
final ArrayList<Integer> result = new ArrayList<Integer>(array.length);
for (int i = 0; i < array.length; i++) {
result.add(Integer.valueOf(array[i]));
}
Collections.sort(result);
return result;
}
}