/*
* RapidMiner
*
* Copyright (C) 2001-2011 by Rapid-I and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapid-i.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.datatable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* This DataTable filters the contained rows using a stack of FilterConditions. Each time the stack is modified, it
* informs it's DataTableFilteredListener that they need to update the table.
*
* @author Sebastian Land
*/
public class FilteredDataTable extends AbstractDataTable implements DataTableListener {
public static interface DataTableFilteredListener {
/**
* This method is called by a datatable, if its content is changed.
*/
public void informDataTableChange(DataTable dataTable);
}
private List<DataTableFilteredListener> listeners = new LinkedList<DataTableFilteredListener>();
private DataTable parentTable;
private int[] selectedIndices;
private int numberOfSelectedRows;
private ArrayList<DataTableFilterCondition> conditionStack = new ArrayList<DataTableFilterCondition>();
public FilteredDataTable(DataTable parentDataTable) {
super(parentDataTable.getName());
this.parentTable = parentDataTable;
// building initial selected indices: All
selectedIndices = new int[parentTable.getNumberOfRows()];
for (int i = 0; i < selectedIndices.length; i++) {
selectedIndices[i] = i;
}
numberOfSelectedRows = parentTable.getNumberOfRows();
parentTable.addDataTableListener(this);
}
@Override
public Iterator<DataTableRow> iterator() {
return new Iterator<DataTableRow>() {
int nextRow = 0;
@Override
public boolean hasNext() {
return nextRow < getNumberOfRows();
}
@Override
public DataTableRow next() {
DataTableRow row = getRow(nextRow);
nextRow++;
return row;
}
@Override
public void remove() {
throw new UnsupportedOperationException("remove() not suppported by FilterDataTable");
}
};
}
@Override
public DataTableRow getRow(int index) {
if (index < numberOfSelectedRows)
return parentTable.getRow(selectedIndices[index]);
else
throw new ArrayIndexOutOfBoundsException("Index exceeds filtered range: " + index);
}
@Override
public int getNumberOfRows() {
return numberOfSelectedRows;
}
public void addCondition(DataTableFilterCondition condtion) {
conditionStack.add(condtion);
updateSelection();
}
public void removeCondition() {
if (conditionStack.size() > 0) {
conditionStack.remove(conditionStack.size() - 1);
updateSelection();
if (conditionStack.size() == 0) {
// building initial selected indices: All
selectedIndices = new int[parentTable.getNumberOfRows()];
for (int i = 0; i < selectedIndices.length; i++) {
selectedIndices[i] = i;
}
numberOfSelectedRows = parentTable.getNumberOfRows();
}
}
}
private void updateSelection() {
int parentRowIndex = 0;
numberOfSelectedRows = 0;
for (DataTableRow row: parentTable) {
boolean keep = true;
for (int conditionIndex = conditionStack.size() - 1; conditionIndex >= 0 && keep; conditionIndex--) {
keep &= conditionStack.get(conditionIndex).keepRow(row);
}
if (keep) {
selectedIndices[numberOfSelectedRows] = parentRowIndex;
numberOfSelectedRows++;
}
parentRowIndex++;
}
// now informing listener of new selection
informDataTableFilteredListener();
}
/*
* Listener Methods
*/
public void addDataTableFilteredListener(DataTableFilteredListener listener) {
this.listeners.add(listener);
}
public void removeDataTableFilteredListewner(DataTableFilteredListener listener) {
this.listeners.remove(listener);
}
private void informDataTableFilteredListener() {
for (DataTableFilteredListener listener: listeners) {
listener.informDataTableChange(this);
}
}
/*
* Delegating methods
*/
@Override
public void add(DataTableRow row) {
parentTable.add(row);
}
@Override
public int getColumnIndex(String name) {
return parentTable.getColumnIndex(name);
}
@Override
public String getColumnName(int i) {
return parentTable.getColumnName(i);
}
@Override
public double getColumnWeight(int i) {
return parentTable.getColumnWeight(i);
}
@Override
public int getNumberOfColumns() {
return parentTable.getNumberOfColumns();
}
@Override
public int getNumberOfSpecialColumns() {
return parentTable.getNumberOfSpecialColumns();
}
@Override
public int getNumberOfValues(int column) {
return parentTable.getNumberOfValues(column);
}
@Override
public boolean isDate(int index) {
return parentTable.isDate(index);
}
@Override
public boolean isDateTime(int index) {
return parentTable.isDateTime(index);
}
@Override
public boolean isNominal(int index) {
return parentTable.isNominal(index);
}
@Override
public boolean isNumerical(int index) {
return parentTable.isNumerical(index);
}
@Override
public boolean isSpecial(int column) {
return parentTable.isSpecial(column);
}
@Override
public boolean isSupportingColumnWeights() {
return parentTable.isSupportingColumnWeights();
}
@Override
public boolean isTime(int index) {
return parentTable.isTime(index);
}
@Override
public String mapIndex(int column, int index) {
return parentTable.mapIndex(column, index);
}
@Override
public int mapString(int column, String value) {
return parentTable.mapString(column, value);
}
@Override
public DataTable sample(int newSize) {
// return parentTable.sample(newSize);
return this;
}
@Override
public void dataTableUpdated(DataTable source) {
fireEvent();
}
}