RowSelectableTableModel proxy for TableModel

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

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

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

/*
 * Copyright 2014 [email protected]
 *
 * 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 = (Boolean) aValue;
      if (value) {

        selected.add(rowIndex);
      }
      else {

        selected.remove(rowIndex);
      }
      super.fireTableCellUpdated(rowIndex, columnIndex);
    }
    else {

      tableModel.setValueAt(aValue, rowIndex, columnIndex - 1);
    }
  }

  @Override
  public void addTableModelListener(TableModelListener listener) {

    super.addTableModelListener(listener);
    tableModel.addTableModelListener(listener);
  }

  @Override
  public void removeTableModelListener(TableModelListener listener) {

    super.removeTableModelListener(listener);
    tableModel.removeTableModelListener(listener);
  }

  private class OriginalModelAdapter implements TableModelListener {

    @Override
    public void tableChanged(TableModelEvent e) {

      if (e.getSource() != tableModel) {

        return;
      }

      if (e.getType() != TableModelEvent.DELETE) {

        return;
      }

      int newRowCount = tableModel.getRowCount();
      boolean selectionUpdated = false;
      if (newRowCount < selectionBasedRowCount) {

        for (Iterator<Integer> iterator = selected.iterator(); iterator.hasNext(); ) {

          if (iterator.next() >= newRowCount) {

            iterator.remove();
            selectionUpdated = true;
          }
        }
      }
      selectionBasedRowCount = newRowCount;

      if (selectionUpdated) {

        RowSelectableTableModel.super.fireTableDataChanged();
      }
    }
  }
}
时间: 2024-08-12 17:22:01

RowSelectableTableModel proxy for TableModel的相关文章

Ubuntu Linux下通过代理(proxy)使用git上github.com

github.com,作为程序员的代码仓库,我们经常会用到.但有时候我们不能直接通过网络链接它,只能通过代理. 这里我有一台代理服务器,起初我以为在终端设置了代理环境就行了,其设置为在你的~/.bashrc里增加以下几行: export http_proxy="http://proxy-server:3128/" export https_proxy="http://proxy-server:3128/" export ftp_proxy="http://

利用tinyproxy在Linux上搭建HTTP Proxy Server

之所以需要用到HTTP Proxy Server并不是为了要翻墙,而是为了让没有公网IP地址的内网主机通过有公网IP地址的外网主机访问Internet.举个例子,阿里云ECS在购买时可以不购买公网IP地址,但这种没有公网IP地址的ECS云主机(实例)是没有访问Internet的能力的,也就是说无法在这台实例上下载文件,这在部署应用如部署MySQL时可能遇到无法完成安装问题.解决的办法有两种,一种是在另一台具有公网访问能力的ECS实例上搭建VPN服务,另一种是在另一台具有公网访问能力的ECS实例上

Forward Proxy &amp; Reverse Proxy | 正向代理 和 反向代理

对请求和响应内容不做修改的转发的服务器,被称为代理服务器.代理服务器分为两种类型:正向代理 和 反向代理. 正向代理:面向互联网,从更广范围获取信息的代理. 反向代理:面向内部,一般用于某企业的网站的前端的代理.反向代理能承担负载均衡,身份认证,内容缓存的任务.这些功能在反向代理上面实现会显得很自然. 正向代理: 如果使用过 vpn 或者 shadowsocks 等FQ工具访问 Google,那么就是在使用正向代理服务器. 下面的图例解释了正向代理的使用.正向代理服务器在互联网中扮演用户的角色,

代理模式(Proxy)

代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问. 在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 代理模式一般涉及到的角色有 抽象角色:声明真实对象和代理对象的共工接口 代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象.同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装. 真实角色:代理角色所代表的真实对

Net Core下通过Proxy 模式

Net Core下通过Proxy 模式 NET Core下的WCF客户端也是开源的,这次发布.NET Core 2.0,同时也发布了 WCF for .NET Core 2.0.0, 本文介绍在.NET Core下如何通过Proxy 消费WCF服务. 我们现在直接可以在 standard 2.0下调用wcf服务了,不过 Microsoft WCF Web Service Reference Provider 目前是beta阶段,要使用这个插件,需要安装一个Visual Studio插件,下载地址

两条proxy switchy的配置规则

原文发表于:2010-12-07 转载至cu于:2012-07-21 今天下午使用chrome的扩展程序proxy switchy做代理,配置切换规则时写了三条规则如下:*twitter.com/**youtube.com/**facebook.com/*使用时,twitter正常访问,有youtube和facebook的站点能访问,但页面不能正常显示.接下来当然是google了,还真有强人遇到这样的问题并解决了.造成上面所描述问题的原因是proxy switchy的配置规则有问题,youtub

Android源代码之DeskClock (三) Proxy/Delegate Application 框架应用

一.概述 当项目有加壳子,插件化或热修复等需求的时候,能够使用Proxy/Delegate Application框架的方式,在正常的模式中,一个程序一般仅仅有一个Application入口,而Proxy/Delegate模式中须要有两个Application,原程序的Application改为Delegate Application,再新加一个Proxy Application,由Proxy Application 提供一系列的个性化定制,再将所有的context和context相关的引用所有

使用mysql proxy对数据库进行读写分离

服务器安排如下: 192.168.100.128 主 192.168.100.129 从 192.168.100.130 mysql-proxy 1.在100.130中下载安装mysql-proxy tar -zxvf mysql-proxy-0.8.5-linux-el6-x86-64bit.tar.gz cp mysql-proxy-0.8.5-linux-el6-x86-64bit /usr/local/mysql-proxy 2.配置环境变量 LUA_PATH="/usr/local/m

Charles Proxy代理使用简要说明

1.去官网下载免费试用版: http://www.charlesproxy.com/ (需要机器有Java运行时)或下载破解注册版:http://charles.iiilab.com/,安装后开启默认代理端口为 本机IP:8888 , 比如: 192.168.43.240:8888 2.手机端http代理设置 iPhone代理设置: 设置 -> 无线局域网 -> 当前连接的wifi 最右侧详细信息按钮 -> 最下面的HTTP代理 -> 手动 -> 填入IP,端口 Androi