/* * Copyright (c) 2011, grossmann, 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.model.table; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.jowidgets.api.model.table.IDefaultTableColumn; import org.jowidgets.api.model.table.IDefaultTableColumnBuilder; import org.jowidgets.api.model.table.IDefaultTableColumnModel; import org.jowidgets.common.image.IImageConstant; import org.jowidgets.common.model.ITableColumnModelListener; import org.jowidgets.common.model.ITableColumnModelObservable; import org.jowidgets.common.types.AlignmentHorizontal; import org.jowidgets.tools.controller.TableColumnModelObservable; import org.jowidgets.util.ArrayUtils; import org.jowidgets.util.Assert; import org.jowidgets.util.event.IChangeListener; class DefaultTableColumnModel implements IDefaultTableColumnModel, ITableColumnModelObservable { private final TableColumnModelObservable tableColumnModelObservable; private final ArrayList<IDefaultTableColumn> columns; private final Map<Integer, IChangeListener> columnChangeListeners; private boolean fireEvents; private boolean eventsFreezed; private ArrayList<IDefaultTableColumn> freezedColumns; private Set<IDefaultTableColumn> modifiedColumns; DefaultTableColumnModel(final int columnCount) { this.eventsFreezed = false; this.fireEvents = true; if (columnCount < 0) { throw new IllegalArgumentException("Column count must be a positive number."); } this.tableColumnModelObservable = new TableColumnModelObservable(); this.columns = new ArrayList<IDefaultTableColumn>(columnCount); this.columnChangeListeners = new HashMap<Integer, IChangeListener>(); for (int i = 0; i < columnCount; i++) { addColumn(); } } @Override public void modifyModelStart() { if (eventsFreezed) { throw new IllegalStateException("The method 'modifyModelStart()' was already invoked."); } this.freezedColumns = new ArrayList<IDefaultTableColumn>(columns); this.modifiedColumns = new HashSet<IDefaultTableColumn>(); this.eventsFreezed = true; } @Override public void modifyModelEnd() { if (!eventsFreezed) { throw new IllegalStateException("The method 'modifyModelStart()' must be invoked first."); } //determine the added columns final List<Integer> addedColumnsIndices = new ArrayList<Integer>(); for (int columnIndex = 0; columnIndex < columns.size(); columnIndex++) { if (!freezedColumns.contains(columns.get(columnIndex))) { addedColumnsIndices.add(Integer.valueOf(columnIndex)); } } //determine the removed columns final List<Integer> removedColumnsIndices = new ArrayList<Integer>(); for (int columnIndex = 0; columnIndex < freezedColumns.size(); columnIndex++) { if (!columns.contains(freezedColumns.get(columnIndex))) { removedColumnsIndices.add(Integer.valueOf(columnIndex)); } } //determine the (residual) modified columns final List<Integer> modifiedColumnIndices = new ArrayList<Integer>(); for (final IDefaultTableColumn column : modifiedColumns) { if (!addedColumnsIndices.contains(column) && !removedColumnsIndices.contains(column)) { final int modifiedColumnIndex = columns.indexOf(column); if (modifiedColumnIndex != -1) { modifiedColumnIndices.add(Integer.valueOf(modifiedColumnIndex)); } } } //now fire the events Collections.sort(addedColumnsIndices); Collections.sort(removedColumnsIndices); Collections.sort(modifiedColumnIndices); final int[] added = ArrayUtils.toArray(addedColumnsIndices); final int[] removed = ArrayUtils.toArray(removedColumnsIndices); final int[] modified = ArrayUtils.toArray(modifiedColumnIndices); if (removed.length > 0 && fireEvents) { tableColumnModelObservable.fireColumnsRemoved(removed); } if (added.length > 0 && fireEvents) { tableColumnModelObservable.fireColumnsAdded(added); } if (modified.length > 0 && fireEvents) { tableColumnModelObservable.fireColumnsChanged(modified); } this.eventsFreezed = false; this.freezedColumns = null; this.modifiedColumns = null; } @Override public void setFireEvents(final boolean fireEvents) { this.fireEvents = fireEvents; } protected boolean isFireEvents() { return fireEvents; } @Override public int getColumnCount() { return columns.size(); } @Override public IDefaultTableColumn getColumn(final int columnIndex) { checkIndex(columnIndex); return columns.get(columnIndex); } @Override public ArrayList<IDefaultTableColumn> getColumns() { return new ArrayList<IDefaultTableColumn>(columns); } @Override public void addColumn(final int columnIndex, final IDefaultTableColumn column) { checkIndexForAdd(columnIndex); columns.add(columnIndex, column); columnAdded(columnIndex, column); } @Override public void setColumn(final int columnIndex, final IDefaultTableColumn column) { checkIndex(columnIndex); columns.set(columnIndex, column); columnChanged(columnIndex); } @Override public void removeColumns(final int... columnIndices) { Assert.paramNotNull(columnIndices, "columnIndices"); //first convert array into a set final Set<Integer> columnsToRemove = new HashSet<Integer>(); for (int i = 0; i < columnIndices.length; i++) { columnsToRemove.add(Integer.valueOf(columnIndices[i])); } //create a new array list that will hold the not removed columns final int newColumnsCount = columns.size() - columnsToRemove.size(); final ArrayList<IDefaultTableColumn> newColumns = new ArrayList<IDefaultTableColumn>(newColumnsCount); //iterate over all columns, add columns that are not removed to the new array list and unregister removed columns for (int columnIndex = 0; columnIndex < columns.size(); columnIndex++) { final IDefaultTableColumn column = columns.get(columnIndex); if (columnsToRemove.contains(Integer.valueOf(columnIndex))) { final IChangeListener changeListener = columnChangeListeners.remove(Integer.valueOf(columnIndex)); if (changeListener != null) { column.removeChangeListener(changeListener); } } else { newColumns.add(column); } } columns.clear(); columns.addAll(newColumns); columnsRemoved(columnIndices); } @Override public ITableColumnModelObservable getTableColumnModelObservable() { return this; } @Override public void addColumnModelListener(final ITableColumnModelListener listener) { tableColumnModelObservable.addColumnModelListener(listener); } @Override public void removeColumnModelListener(final ITableColumnModelListener listener) { tableColumnModelObservable.removeColumnModelListener(listener); } //////////////////////////////////////////////////////////////////////////////////////////////// //Convenience methods start //////////////////////////////////////////////////////////////////////////////////////////////// @Override public IDefaultTableColumn addColumn() { return addColumn(columns.size()); } @Override public IDefaultTableColumn addColumn(final int columnIndex) { final IDefaultTableColumn result = new DefaultTableColumnBuilder().build(); addColumn(columnIndex, result); return result; } @Override public void addColumn(final IDefaultTableColumn column) { addColumn(columns.size(), column); } @Override public IDefaultTableColumn addColumn(final IDefaultTableColumnBuilder columnBuilder) { return addColumn(columns.size(), columnBuilder); } @Override public IDefaultTableColumn addColumn(final int columnIndex, final IDefaultTableColumnBuilder columnBuilder) { final IDefaultTableColumn result = columnBuilder.build(); addColumn(columnIndex, result); return result; } @Override public IDefaultTableColumn addColumn(final String text) { return addColumn(new DefaultTableColumnBuilder().setText(text)); } @Override public IDefaultTableColumn addColumn(final String text, final String toolTipText) { return addColumn(new DefaultTableColumnBuilder().setText(text).setToolTipText(toolTipText)); } @Override public IDefaultTableColumn setColumn(final int columnIndex, final IDefaultTableColumnBuilder columnBuilder) { checkIndex(columnIndex); final IDefaultTableColumn result = columnBuilder.build(); setColumn(columnIndex, result); return result; } @Override public void setColumnText(final int columnIndex, final String text) { checkIndex(columnIndex); getColumn(columnIndex).setText(text); } @Override public void setColumnToolTipText(final int columnIndex, final String tooltipText) { checkIndex(columnIndex); getColumn(columnIndex).setToolTipText(tooltipText); } @Override public void setColumnIcon(final int columnIndex, final IImageConstant icon) { checkIndex(columnIndex); getColumn(columnIndex).setIcon(icon); } @Override public void setColumnAlignment(final int columnIndex, final AlignmentHorizontal alignment) { checkIndex(columnIndex); getColumn(columnIndex).setAlignment(alignment); } @Override public void removeColumn(final int columnIndex) { removeColumns(new int[] {columnIndex}); } @Override public void removeColumns(final int fromColumnIndex, final int toColumnIndex) { if (fromColumnIndex > toColumnIndex) { throw new IllegalArgumentException("From index must be less or equal than to index."); } checkIndex(fromColumnIndex); checkIndex(toColumnIndex); final int[] indices = new int[1 + toColumnIndex - fromColumnIndex]; for (int i = 0; i < indices.length; i++) { indices[i] = fromColumnIndex + i; } removeColumns(indices); } @Override public void removeAllColumns() { if (columns.size() > 0) { removeColumns(0, columns.size() - 1); } } //////////////////////////////////////////////////////////////////////////////////////////////// //Convenience methods end //////////////////////////////////////////////////////////////////////////////////////////////// private void columnAdded(final int columnIndex, final IDefaultTableColumn column) { final ColumnChangeListener changeListener = new ColumnChangeListener(columnIndex); columnChangeListeners.put(Integer.valueOf(columnIndex), changeListener); column.addChangeListener(changeListener); if (!eventsFreezed && fireEvents) { tableColumnModelObservable.fireColumnsAdded(new int[] {columnIndex}); } } protected boolean isEventsFreezed() { return eventsFreezed; } private void columnChanged(final int columnIndex) { if (!eventsFreezed && fireEvents) { tableColumnModelObservable.fireColumnsChanged(new int[] {columnIndex}); } else if (eventsFreezed) { modifiedColumns.add(columns.get(columnIndex)); } } private void columnsRemoved(final int[] columnIndices) { if (!eventsFreezed && fireEvents) { tableColumnModelObservable.fireColumnsRemoved(columnIndices); } } private void checkIndexForAdd(final int columnIndex) { checkIndex(columnIndex, columns.size()); } private void checkIndex(final int columnIndex) { checkIndex(columnIndex, columns.size() - 1); } private void checkIndex(final int columnIndex, final int maxIndex) { if (columnIndex < 0 || columnIndex > maxIndex) { throw new IllegalArgumentException( "Column index must be between '" + 0 + "' and '" + maxIndex + "', but is '" + columnIndex + "'."); } } class ColumnChangeListener implements IChangeListener { private final int columnIndex; ColumnChangeListener(final int columnIndex) { super(); this.columnIndex = columnIndex; } @Override public void changed() { columnChanged(columnIndex); } } }