package prefuse.data.util;
import java.util.Comparator;
import prefuse.data.Table;
import prefuse.data.column.Column;
import prefuse.data.event.ColumnListener;
import prefuse.data.event.EventConstants;
import prefuse.data.event.TableListener;
import prefuse.util.collections.BooleanIntSortedMap;
import prefuse.util.collections.DoubleIntSortedMap;
import prefuse.util.collections.FloatIntSortedMap;
import prefuse.util.collections.IncompatibleComparatorException;
import prefuse.util.collections.IntIntSortedMap;
import prefuse.util.collections.IntIterator;
import prefuse.util.collections.IntSortedMap;
import prefuse.util.collections.LongIntSortedMap;
import prefuse.util.collections.ObjectIntSortedMap;
import prefuse.util.collections.SortedMapFactory;
/**
* Index instance that uses red-black trees to provide an index
* over a column of data.
*
* @author <a href="http://jheer.org">jeffrey heer</a>
*/
public class TreeIndex implements Index, ColumnListener, TableListener {
protected Table m_table;
protected RowManager m_rows;
protected Column m_col;
protected IntSortedMap m_index;
protected boolean m_reindex;
protected int m_colidx;
/**
* Create a new TreeIndex.
* @param t the Table containing the data column to index
* @param rows the RowManager of the Table
* @param col the Column instance to index
* @param cmp the Comparator to use to sort data values
* @throws IncompatibleComparatorException if the comparator is not
* compatible with the column's data type
*/
public TreeIndex(Table t, RowManager rows, Column col, Comparator cmp)
throws IncompatibleComparatorException
{
m_table = t;
m_rows = rows;
m_col = col;
m_index = SortedMapFactory.getMap(col.getColumnType(), cmp, false);
index();
m_col.addColumnListener(this);
m_table.addTableListener(this);
}
/**
* @see prefuse.data.util.Index#dispose()
*/
public void dispose() {
m_col.removeColumnListener(this);
m_table.removeTableListener(this);
}
/**
* @see prefuse.data.util.Index#getComparator()
*/
public Comparator getComparator() {
return m_index.comparator();
}
/**
* @see prefuse.data.util.Index#size()
*/
public int size() {
return m_index.size();
}
private int getColumnIndex() {
if ( !(m_table.getColumn(m_colidx) == m_col) ) {
m_colidx = m_table.getColumnNumber(m_col);
}
return m_colidx;
}
// ------------------------------------------------------------------------
// Index Update Methods
/**
* @see prefuse.data.util.Index#index()
*/
public void index() {
m_index.clear();
// iterate over all valid values, adding them to the index
int idx = getColumnIndex();
m_colidx = idx;
IntIterator rows = m_rows.rows();
if ( m_index instanceof IntIntSortedMap )
{
IntIntSortedMap map = (IntIntSortedMap)m_index;
while ( rows.hasNext() ) {
int r = rows.nextInt();
map.put(m_col.getInt(m_table.getColumnRow(r,idx)), r);
}
}
else if ( m_index instanceof LongIntSortedMap )
{
LongIntSortedMap map = (LongIntSortedMap)m_index;
while ( rows.hasNext() ) {
int r = rows.nextInt();
map.put(m_col.getLong(m_table.getColumnRow(r,idx)), r);
}
}
else if ( m_index instanceof FloatIntSortedMap )
{
FloatIntSortedMap map = (FloatIntSortedMap)m_index;
while ( rows.hasNext() ) {
int r = rows.nextInt();
map.put(m_col.getFloat(m_table.getColumnRow(r,idx)), r);
}
}
else if ( m_index instanceof DoubleIntSortedMap )
{
DoubleIntSortedMap map = (DoubleIntSortedMap)m_index;
while ( rows.hasNext() ) {
int r = rows.nextInt();
map.put(m_col.getDouble(m_table.getColumnRow(r,idx)), r);
}
}
else if ( m_index instanceof BooleanIntSortedMap )
{
BooleanIntSortedMap map = (BooleanIntSortedMap)m_index;
while ( rows.hasNext() ) {
int r = rows.nextInt();
map.put(m_col.getBoolean(m_table.getColumnRow(r,idx)), r);
}
}
else if ( m_index instanceof ObjectIntSortedMap )
{
ObjectIntSortedMap map = (ObjectIntSortedMap)m_index;
while ( rows.hasNext() ) {
int r = rows.nextInt();
map.put(m_col.get(m_table.getColumnRow(r,idx)), r);
}
}
else {
throw new IllegalStateException();
}
m_reindex = false;
}
// ------------------------------------------------------------------------
// Listener Methods
/**
* @see prefuse.data.event.TableListener#tableChanged(prefuse.data.Table, int, int, int, int)
*/
public void tableChanged(Table t, int start, int end, int col, int type) {
if ( type == EventConstants.UPDATE || t != m_table
|| col != EventConstants.ALL_COLUMNS )
return;
boolean insert = (type==EventConstants.INSERT);
for ( int r=start; r<=end; ++r )
rowChanged(r, insert);
}
private void rowChanged(int row, boolean insert) {
// make sure we access the right column value
int crow = m_rows.getColumnRow(row, getColumnIndex());
if ( m_index instanceof IntIntSortedMap )
{
IntIntSortedMap map = (IntIntSortedMap)m_index;
int key = m_col.getInt(row);
if ( insert )
map.put(key, row);
else
map.remove(key, row);
}
else if ( m_index instanceof LongIntSortedMap )
{
LongIntSortedMap map = (LongIntSortedMap)m_index;
long key = m_col.getLong(crow);
if ( insert )
map.put(key, row);
else
map.remove(key, row);
}
else if ( m_index instanceof FloatIntSortedMap )
{
FloatIntSortedMap map = (FloatIntSortedMap)m_index;
float key = m_col.getFloat(crow);
if ( insert )
map.put(key, row);
else
map.remove(key, row);
}
else if ( m_index instanceof DoubleIntSortedMap )
{
DoubleIntSortedMap map = (DoubleIntSortedMap)m_index;
double key = m_col.getDouble(crow);
if ( insert )
map.put(key, row);
else
map.remove(key, row);
}
else if ( m_index instanceof BooleanIntSortedMap )
{
BooleanIntSortedMap map = (BooleanIntSortedMap)m_index;
boolean key = m_col.getBoolean(crow);
if ( insert )
map.put(key, row);
else
map.remove(key, row);
}
else if ( m_index instanceof ObjectIntSortedMap )
{
ObjectIntSortedMap map = (ObjectIntSortedMap)m_index;
Object key = m_col.get(crow);
if ( insert )
map.put(key, row);
else
map.remove(key, row);
}
else {
throw new IllegalStateException();
}
}
/**
* @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, int, int)
*/
public void columnChanged(Column src, int type, int start, int end) {
m_reindex = true;
}
/**
* @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, boolean)
*/
public void columnChanged(Column src, int idx, boolean prev) {
int row = m_rows.getTableRow(idx, getColumnIndex());
if ( row < 0 ) return; // invalid row value
((BooleanIntSortedMap)m_index).remove(prev, row);
((BooleanIntSortedMap)m_index).put(src.getBoolean(idx), row);
}
/**
* @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, int)
*/
public void columnChanged(Column src, int idx, int prev) {
int row = m_rows.getTableRow(idx, getColumnIndex());
if ( row < 0 ) return; // invalid row value
((IntIntSortedMap)m_index).remove(prev, row);
((IntIntSortedMap)m_index).put(src.getInt(idx), row);
}
/**
* @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, long)
*/
public void columnChanged(Column src, int idx, long prev) {
int row = m_rows.getTableRow(idx, getColumnIndex());
if ( row < 0 ) return; // invalid row value
((LongIntSortedMap)m_index).remove(prev, row);
((LongIntSortedMap)m_index).put(src.getLong(idx), row);
}
/**
* @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, float)
*/
public void columnChanged(Column src, int idx, float prev) {
int row = m_rows.getTableRow(idx, getColumnIndex());
if ( row < 0 ) return; // invalid row value
((FloatIntSortedMap)m_index).remove(prev, row);
((FloatIntSortedMap)m_index).put(src.getFloat(idx), row);
}
/**
* @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, double)
*/
public void columnChanged(Column src, int idx, double prev) {
int row = m_rows.getTableRow(idx, getColumnIndex());
if ( row < 0 ) return; // invalid row value
((DoubleIntSortedMap)m_index).remove(prev, row);
((DoubleIntSortedMap)m_index).put(src.getDouble(idx), row);
}
/**
* @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, java.lang.Object)
*/
public void columnChanged(Column src, int idx, Object prev) {
int row = m_rows.getTableRow(idx, getColumnIndex());
if ( row < 0 ) return; // invalid row value
((ObjectIntSortedMap)m_index).remove(prev, row);
((ObjectIntSortedMap)m_index).put(src.get(idx), row);
}
// ------------------------------------------------------------------------
// Retrieval Methods
/**
* @see prefuse.data.util.Index#minimum()
*/
public int minimum() {
return m_index.getMinimum();
}
/**
* @see prefuse.data.util.Index#maximum()
*/
public int maximum() {
return m_index.getMaximum();
}
/**
* @see prefuse.data.util.Index#median()
*/
public int median() {
return m_index.getMedian();
}
/**
* @see prefuse.data.util.Index#uniqueCount()
*/
public int uniqueCount() {
return m_index.getUniqueCount();
}
// ------------------------------------------------------------------------
/**
* @see prefuse.data.util.Index#allRows(int)
*/
public IntIterator allRows(int type) {
boolean ascending = (type & Index.TYPE_ASCENDING) > 0;
return m_index.valueIterator(ascending);
}
/**
* @see prefuse.data.util.Index#rows(java.lang.Object, java.lang.Object, int)
*/
public IntIterator rows(Object lo, Object hi, int type) {
if ( !(m_index instanceof ObjectIntSortedMap) )
throw new IllegalStateException();
boolean reverse = (type & Index.TYPE_DESCENDING) > 0;
boolean linc = (type & Index.TYPE_LEFT_INCLUSIVE) > 0;
boolean hinc = (type & Index.TYPE_RIGHT_INCLUSIVE) > 0;
if ( lo == null ) lo = ObjectIntSortedMap.MIN_KEY;
if ( hi == null ) hi = ObjectIntSortedMap.MAX_KEY;
ObjectIntSortedMap index = (ObjectIntSortedMap)m_index;
if ( reverse ) {
return index.valueRangeIterator(hi, hinc, lo, linc);
} else {
return index.valueRangeIterator(lo, linc, hi, hinc);
}
}
/**
* @see prefuse.data.util.Index#rows(int, int, int)
*/
public IntIterator rows(int lo, int hi, int type) {
if ( !(m_index instanceof IntIntSortedMap) )
throw new IllegalStateException();
boolean reverse = (type & Index.TYPE_DESCENDING) > 0;
boolean linc = (type & Index.TYPE_LEFT_INCLUSIVE) > 0;
boolean hinc = (type & Index.TYPE_RIGHT_INCLUSIVE) > 0;
IntIntSortedMap index = (IntIntSortedMap)m_index;
if ( reverse ) {
return index.valueRangeIterator(hi, hinc, lo, linc);
} else {
return index.valueRangeIterator(lo, linc, hi, hinc);
}
}
/**
* @see prefuse.data.util.Index#rows(long, long, int)
*/
public IntIterator rows(long lo, long hi, int type) {
if ( !(m_index instanceof LongIntSortedMap) )
throw new IllegalStateException();
boolean reverse = (type & Index.TYPE_DESCENDING) > 0;
boolean linc = (type & Index.TYPE_LEFT_INCLUSIVE) > 0;
boolean hinc = (type & Index.TYPE_RIGHT_INCLUSIVE) > 0;
LongIntSortedMap index = (LongIntSortedMap)m_index;
if ( reverse ) {
return index.valueRangeIterator(hi, hinc, lo, linc);
} else {
return index.valueRangeIterator(lo, linc, hi, hinc);
}
}
/**
* @see prefuse.data.util.Index#rows(float, float, int)
*/
public IntIterator rows(float lo, float hi, int type) {
if ( !(m_index instanceof FloatIntSortedMap) )
throw new IllegalStateException();
boolean reverse = (type & Index.TYPE_DESCENDING) > 0;
boolean linc = (type & Index.TYPE_LEFT_INCLUSIVE) > 0;
boolean hinc = (type & Index.TYPE_RIGHT_INCLUSIVE) > 0;
FloatIntSortedMap index = (FloatIntSortedMap)m_index;
if ( reverse ) {
return index.valueRangeIterator(hi, hinc, lo, linc);
} else {
return index.valueRangeIterator(lo, linc, hi, hinc);
}
}
/**
* @see prefuse.data.util.Index#rows(double, double, int)
*/
public IntIterator rows(double lo, double hi, int type) {
if ( !(m_index instanceof DoubleIntSortedMap) )
throw new IllegalStateException();
boolean reverse = (type & Index.TYPE_DESCENDING) > 0;
boolean linc = (type & Index.TYPE_LEFT_INCLUSIVE) > 0;
boolean hinc = (type & Index.TYPE_RIGHT_INCLUSIVE) > 0;
DoubleIntSortedMap index = (DoubleIntSortedMap)m_index;
if ( reverse ) {
return index.valueRangeIterator(hi, hinc, lo, linc);
} else {
return index.valueRangeIterator(lo, linc, hi, hinc);
}
}
// ------------------------------------------------------------------------
/**
* @see prefuse.data.util.Index#rows(int)
*/
public IntIterator rows(int val) {
return rows(val, val, Index.TYPE_AII);
}
/**
* @see prefuse.data.util.Index#rows(long)
*/
public IntIterator rows(long val) {
return rows(val, val, Index.TYPE_AII);
}
/**
* @see prefuse.data.util.Index#rows(float)
*/
public IntIterator rows(float val) {
return rows(val, val, Index.TYPE_AII);
}
/**
* @see prefuse.data.util.Index#rows(double)
*/
public IntIterator rows(double val) {
return rows(val, val, Index.TYPE_AII);
}
/**
* @see prefuse.data.util.Index#rows(boolean)
*/
public IntIterator rows(boolean val) {
if ( !(m_index instanceof BooleanIntSortedMap) )
throw new IllegalStateException();
BooleanIntSortedMap index = (BooleanIntSortedMap)m_index;
return index.valueRangeIterator(val, true, val, true);
}
/**
* @see prefuse.data.util.Index#rows(java.lang.Object)
*/
public IntIterator rows(Object val) {
return rows(val, val, Index.TYPE_AII);
}
// ------------------------------------------------------------------------
/**
* @see prefuse.data.util.Index#get(double)
*/
public int get(double x) {
DoubleIntSortedMap index = (DoubleIntSortedMap)m_index;
return index.get(x);
}
/**
* @see prefuse.data.util.Index#get(float)
*/
public int get(float x) {
FloatIntSortedMap index = (FloatIntSortedMap)m_index;
return index.get(x);
}
/**
* @see prefuse.data.util.Index#get(int)
*/
public int get(int x) {
IntIntSortedMap index = (IntIntSortedMap)m_index;
return index.get(x);
}
/**
* @see prefuse.data.util.Index#get(long)
*/
public int get(long x) {
LongIntSortedMap index = (LongIntSortedMap)m_index;
return index.get(x);
}
/**
* @see prefuse.data.util.Index#get(java.lang.Object)
*/
public int get(Object x) {
ObjectIntSortedMap index = (ObjectIntSortedMap)m_index;
return index.get(x);
}
} // end of class ColumnIndex