filebot/source/net/filebot/ui/sfv/ChecksumTableModel.java

335 lines
7.0 KiB
Java

package net.filebot.ui.sfv;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.table.AbstractTableModel;
import net.filebot.hash.HashType;
import net.filebot.util.FastFile;
import net.filebot.util.FileUtilities;
class ChecksumTableModel extends AbstractTableModel {
private final IndexedMap<String, ChecksumRow> rows = new IndexedMap<String, ChecksumRow>() {
@Override
public String key(ChecksumRow value) {
return value.getName();
}
};
private final List<File> checksumColumns = new ArrayList<File>(4);
public static final String HASH_TYPE_PROPERTY = "hashType";
private HashType hashType = HashType.SFV;
@Override
public String getColumnName(int columnIndex) {
switch (columnIndex) {
case 0:
return "State";
case 1:
return "Name";
default:
// works for files too and simply returns the name unchanged
return FileUtilities.getFolderName(getColumnRoot(columnIndex));
}
}
@Override
public Class<?> getColumnClass(int columnIndex) {
switch (columnIndex) {
case 0:
return ChecksumRow.State.class;
case 1:
return String.class;
default:
return ChecksumCell.class;
}
}
protected int getColumnIndex(ChecksumCell cell) {
int index = checksumColumns.indexOf(cell.getRoot());
if (index < 0)
return -1;
// add checksum column offset
return index + 2;
}
public File getColumnRoot(int columnIndex) {
// substract checksum column offset
return checksumColumns.get(columnIndex - 2);
}
public boolean isVerificationColumn(int columnIndex) {
return columnIndex >= 2 && getColumnRoot(columnIndex).isFile();
}
public List<File> getChecksumColumns() {
return Collections.unmodifiableList(checksumColumns);
}
@Override
public int getColumnCount() {
// add checksum column offset
return checksumColumns.size() + 2;
}
protected int getRowIndex(ChecksumRow row) {
return rows.getIndexByKey(row.getName());
}
protected int getRowIndex(ChecksumCell cell) {
return rows.getIndexByKey(cell.getName());
}
public List<ChecksumRow> rows() {
return Collections.unmodifiableList(rows);
}
@Override
public int getRowCount() {
return rows.size();
}
public void setHashType(HashType hashType) {
HashType old = this.hashType;
this.hashType = hashType;
// update table
fireTableDataChanged();
// notify listeners
pcs.firePropertyChange(HASH_TYPE_PROPERTY, old, hashType);
}
public HashType getHashType() {
return hashType;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
ChecksumRow row = rows.get(rowIndex);
switch (columnIndex) {
case 0:
return row.getState();
case 1:
return row.getName();
}
ChecksumCell cell = row.getChecksum(getColumnRoot(columnIndex));
// empty cell
if (cell == null)
return null;
switch (cell.getState()) {
case READY:
return cell.getChecksum(hashType);
case ERROR:
return cell.getError();
default: // PENDING or PROGRESS
return cell.getTask();
}
}
public void addAll(Collection<ChecksumCell> values) {
List<ChecksumCell> replacements = new ArrayList<ChecksumCell>();
int rowCount = getRowCount();
int columnCount = getColumnCount();
for (ChecksumCell cell : values) {
int rowIndex = getRowIndex(cell);
ChecksumRow row;
if (rowIndex >= 0) {
// get existing row
row = rows.get(rowIndex);
} else {
// add new row
row = new ChecksumRow(cell.getName());
row.addPropertyChangeListener(stateListener);
rows.add(row);
}
// add cell to row
ChecksumCell old = row.put(cell);
// dispose of old cell
if (old != null) {
old.dispose();
replacements.add(cell);
}
// listen to changes (progress, state)
cell.addPropertyChangeListener(progressListener);
if (!checksumColumns.contains(cell.getRoot())) {
checksumColumns.add(new FastFile(cell.getRoot().getPath()));
}
}
// fire table events
if (columnCount != getColumnCount()) {
// number of columns has changed
fireTableStructureChanged();
return;
}
for (ChecksumCell replacement : replacements) {
int row = getRowIndex(replacement);
// update this row
fireTableRowsUpdated(row, row);
}
if (rowCount != getRowCount()) {
// some rows have been inserted
fireTableRowsInserted(rowCount, getRowCount() - 1);
}
}
public void remove(int... index) {
// sort index array
Arrays.sort(index);
for (int i : index) {
rows.get(i).dispose();
}
// remove rows
rows.removeAll(index);
fireTableRowsDeleted(index[0], index[index.length - 1]);
}
public void clear() {
checksumColumns.clear();
rows.clear();
fireTableStructureChanged();
}
private final PropertyChangeListener stateListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
int row = getRowIndex((ChecksumRow) evt.getSource());
if (row >= 0) {
// update row
fireTableRowsUpdated(row, row);
}
}
};
private final PropertyChangeListener progressListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
ChecksumCell cell = (ChecksumCell) evt.getSource();
int row = getRowIndex(cell);
int column = getColumnIndex(cell);
if (row >= 0 && column >= 0) {
fireTableCellUpdated(row, column);
}
}
};
protected static abstract class IndexedMap<K, V> extends AbstractList<V> {
private final Map<K, Integer> indexMap = new HashMap<K, Integer>(64);
private final List<V> list = new ArrayList<V>(64);
public abstract K key(V value);
@Override
public V get(int index) {
return list.get(index);
}
public int getIndexByKey(K key) {
Integer index = indexMap.get(key);
if (index == null)
return -1;
return index;
}
@Override
public boolean add(V value) {
K key = key(value);
Integer index = indexMap.get(key);
if (index == null && list.add(value)) {
indexMap.put(key, lastIndexOf(value));
return true;
}
return false;
}
public void removeAll(int... index) {
// sort index array
Arrays.sort(index);
// remove in reverse
for (int i = index.length - 1; i >= 0; i--) {
V value = list.remove(index[i]);
indexMap.remove(key(value));
}
updateIndexMap();
}
private void updateIndexMap() {
for (int i = 0; i < list.size(); i++) {
indexMap.put(key(list.get(i)), i);
}
}
@Override
public int size() {
return list.size();
}
@Override
public void clear() {
list.clear();
indexMap.clear();
}
}
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
}
}