// This file is part of PleoCommand:
// Interactively control Pleo with psychobiological parameters
//
// Copyright (C) 2010 Oliver Hoffmann - Hoffmann_Oliver@gmx.de
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Boston, USA.
package pleocmd.itfc.gui.dse;
import java.awt.Color;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NavigableMap;
import java.util.TreeMap;
import javax.swing.table.AbstractTableModel;
import pleocmd.Log;
import pleocmd.exc.FormatException;
import pleocmd.itfc.gui.dse.HexTableCellRenderer.Cell;
import pleocmd.pipe.data.Data;
import pleocmd.pipe.val.Syntax;
public abstract class HexTableModel extends AbstractTableModel {
private static final long serialVersionUID = 3288312058227312945L;
private int columnCount;
private int rowCount;
private RandomAccess stream;
private final NavigableMap<Long, Color[]> map;
private boolean modified;
public HexTableModel() {
map = new TreeMap<Long, Color[]>();
}
public final void updateColumnCount(final int newColumnCount) {
columnCount = newColumnCount;
updateRowCount();
}
public final void updateRowCount() {
long size;
try {
size = stream == null ? 0 : stream.length();
rowCount = columnCount == 0 ? 0 : (int) (size / columnCount + (size
% columnCount > 0 ? 1 : 0));
fireTableStructureChanged();
} catch (final IOException e) {
columnCount = 0;
rowCount = 0;
}
}
public final void setStream(final RandomAccess stream) {
map.clear();
if (this.stream != null) try {
this.stream.close();
} catch (final IOException e) {
Log.error(e, "Cannot free resources");
}
this.stream = stream;
updateRowCount();
resetModification();
}
public final RandomAccess getStream() {
return stream;
}
@Override
public final int getColumnCount() {
return columnCount;
}
@Override
public final int getRowCount() {
return rowCount;
}
@Override
public final Cell getValueAt(final int rowIndex, final int columnIndex) {
if (stream == null) return new Cell("", Color.BLACK);
final long pos = (long) rowIndex * columnCount + columnIndex;
Long startPos = map.floorKey(pos);
if (startPos == null) {
parse(pos);
startPos = map.floorKey(pos);
}
Color[] ca;
int relPos;
if (startPos == null) {
ca = new Color[] { Color.RED };
relPos = 0;
} else {
ca = map.get(startPos);
relPos = (int) (pos - startPos);
}
if (relPos >= ca.length) {
parse(pos);
startPos = map.floorKey(pos);
ca = map.get(startPos);
relPos = (int) (pos - startPos);
}
try {
stream.seek(pos);
return new Cell(String.format("%02X", stream.read()),
relPos < ca.length ? ca[relPos] : Color.RED);
} catch (final IOException e) {
return new Cell("", Color.BLACK);
}
}
@Override
public final boolean isCellEditable(final int rowIndex,
final int columnIndex) {
return true;
}
@Override
public final void setValueAt(final Object aValue, final int rowIndex,
final int columnIndex) {
if (aValue instanceof Cell)
editing(((Cell) aValue).getString(), rowIndex, columnIndex);
}
private void parse(final long endPos) {
final long pos = map.isEmpty() ? 0 : map.lastKey()
+ map.lastEntry().getValue().length;
try {
stream.seek(pos);
long startPos;
while ((startPos = stream.getFilePointer()) <= endPos
&& startPos < stream.length()) {
final List<Syntax> syntaxList = new ArrayList<Syntax>();
try {
Data.createFromBinary(stream.getDataInput(), syntaxList);
} catch (final FormatException e) {
// already handled in the resulting syntax-list
}
final Color[] ca = new Color[(int) (stream.getFilePointer() - startPos)];
int i = 0;
Color c = Color.RED;
for (final Syntax stx : syntaxList) {
for (; i < stx.getPosition(); ++i)
ca[i] = c;
c = stx.getType().getColor();
}
for (; i < ca.length; ++i)
ca[i] = c;
map.put(startPos, ca);
}
} catch (final IOException e) {
Log.error(e);
}
}
private void update(final long pos) {
final Long startPos = map.floorKey(pos);
if (startPos != null) {
final Iterator<Color[]> it = map.tailMap(startPos).values()
.iterator();
while (it.hasNext()) {
it.next();
it.remove();
}
fireTableDataChanged();
}
}
public final boolean isModified() {
return modified;
}
protected abstract void stateChanged();
final void editing(final String value, final int rowIndex,
final int columnIndex) {
final long pos = (long) rowIndex * columnCount + columnIndex;
try {
stream.seek(pos);
stream.write(Integer.valueOf(value, 16));
} catch (final IOException e) {
Log.error(e);
}
modified = true;
stateChanged();
update(pos);
}
public final void resetModification() {
modified = false;
stateChanged();
}
}