首页 > 代码库 > RowSelectableTableModel proxy for TableModel

RowSelectableTableModel proxy for TableModel


群里看到有的同学需要在某 JTable 内容的基础上,多出一列 checkbox,用于某种多选,不是第一次看到这种需求了,所以写了这个“通用”的代理实现。


大体写了一下,没有仔细处理原TableModel的事件,所以使用的时候要注意原TableModel不能fire基于cell的更新事件,不能添加删除列。


先扔这里,有时间再琢磨。


/*
 * Copyright 2014 raistlic@gmail.com
 *
 * 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.
 */

import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * Currently not supporting column insertion or deletion, that is, if the wrapped (original) table
 * model inserted or deleted a column, not sure if the table will correctly pick up the event and
 * works fine afterwards, behaviour unknown.
 *
 * <p/>
 * Also not supporting cell based update event which column index is greater than the selectable
 * column.
 *
 * TODO: Builder and its methods not Java doc-ed yet.
 * TODO: properly delegate & translate events for the original table model.
 *
 * @author raistlic
 * @since 2014-08-26
 */
public class RowSelectableTableModel extends AbstractTableModel implements TableModel {

  public static RowSelectableTableModel wrap(TableModel tableModel) {

    return builderFor(tableModel).build();
  }

  public static Builder builderFor(TableModel tableModel) {

    if (tableModel == null) {

      throw new NullPointerException("'tableModel' is null.");
    }

    return new Builder(tableModel);
  }

  public static class Builder {

    private TableModel tableModel;

    private int limit;

    private int selectableColumnIndex;

    private String selectableColumnName;

    private Builder(TableModel tableModel) {

      this.tableModel = tableModel;
      this.limit = 0;

      selectableColumnIndex = 0;
      selectableColumnName = "Select";
    }

    public Builder withSelectionLimit(int limit) {

      this.limit = limit;
      return this;
    }

    public Builder withSelectableColumnAt(int column) {

      if (column < 0) {

        throw new IndexOutOfBoundsException("Selectable column index out of bounds: " + column);
      }

      if (column > tableModel.getColumnCount()) {

        throw new IndexOutOfBoundsException(
                "Selectable column index out of bounds: " + column + " / " + tableModel.getColumnCount());
      }

      this.selectableColumnIndex = column;
      return this;
    }

    public Builder withSelectableColumnName(String name) {

      this.selectableColumnName = (name == null) ? "" : name;
      return this;
    }

    public RowSelectableTableModel build() {

      RowSelectableTableModel result = new RowSelectableTableModel(this);
      tableModel.addTableModelListener(result.new OriginalModelAdapter());
      return result;
    }
  }

  private final TableModel tableModel;

  private final int limit;

  private final int selectableColumnIndex;

  private final String selectableColumnName;

  private Set<Integer> selected;

  private int selectionBasedRowCount;

  private RowSelectableTableModel(Builder builder) {

    this.tableModel = builder.tableModel;
    this.limit = builder.limit;
    this.selectableColumnIndex = builder.selectableColumnIndex;
    this.selectableColumnName = builder.selectableColumnName;

    this.selectionBasedRowCount = tableModel.getRowCount();
    this.selected = new HashSet<Integer>();
  }

  public Set<Integer> getSelectedRows() {

    // defensive copy:
    return new HashSet<Integer>(selected);
  }

  @Override
  public int getRowCount() {

    return tableModel.getRowCount();
  }

  @Override
  public int getColumnCount() {

    return 1 + tableModel.getColumnCount();
  }

  @Override
  public String getColumnName(int columnIndex) {

    if (columnIndex < selectableColumnIndex) {

      return tableModel.getColumnName(columnIndex);
    }
    else if (columnIndex == selectableColumnIndex) {

      return selectableColumnName;
    }
    else {

      return tableModel.getColumnName(columnIndex - 1);
    }
  }

  @Override
  public Class<?> getColumnClass(int columnIndex) {

    if (columnIndex < selectableColumnIndex) {

      return tableModel.getColumnClass(columnIndex);
    }
    else if (columnIndex == selectableColumnIndex) {

      return boolean.class;
    }
    else {

      return tableModel.getColumnClass(columnIndex - 1);
    }
  }

  @Override
  public boolean isCellEditable(int rowIndex, int columnIndex) {

    if (columnIndex < selectableColumnIndex) {

      return tableModel.isCellEditable(rowIndex, columnIndex);
    }
    else if (columnIndex == selectableColumnIndex) {

      return limit <= 0 || selected.size() < limit;
    }
    else {

      return tableModel.isCellEditable(rowIndex, columnIndex - 1);
    }
  }

  @Override
  public Object getValueAt(int rowIndex, int columnIndex) {

    if (columnIndex < selectableColumnIndex) {

      return tableModel.getValueAt(rowIndex, columnIndex);
    }
    else if (columnIndex == selectableColumnIndex) {

      return selected.contains(rowIndex);
    }
    else {

      return tableModel.getValueAt(rowIndex, columnIndex - 1);
    }
  }

  @Override
  public void setValueAt(Object aValue, int rowIndex, int columnIndex) {

    if (columnIndex < selectableColumnIndex) {

      tableModel.setValueAt(aValue, rowIndex, columnIndex);
    }
    else if (columnIndex == selectableColumnIndex) {

      @SuppressWarnings("unchecked")
      boolean value = http://www.mamicode.com/(Boolean) aValue;>

RowSelectableTableModel proxy for TableModel