package matrix;
import java.util.ArrayList;
import java.util.List;
import org.molgenis.core.Nameable;
import org.molgenis.framework.db.Database;
import org.molgenis.framework.db.Query;
import org.molgenis.framework.db.QueryRule;
import org.molgenis.framework.db.QueryRule.Operator;
public class AbstractDataMatrixQueries
{
/**
* Apply filters (query rules) to the values of a matrix, to get a new
* subset matrix back. The 'field' specifies the row name where the filter
* is applied. And as expected: The 'operator' is the comparator, and
* 'value' is the point of reference.
*
* @param dm
* @param rules
* @return AbstractDataMatrixInstance
* @throws Exception
*/
public static DataMatrixInstance getSubMatrixFilterByRowMatrixValues(DataMatrixInstance dm, QueryRule... rules)
throws Exception
{
checkQueryRulesOld(dm, false, rules);
// colNames is the resultset we want to get
List<String> colNames = null;
List<String> rowNames = dm.getRowNames();
// iterate over queryrules
for (QueryRule rule : rules)
{
List<String> result = null;
if (dm.getData().getValueType().equals("Decimal"))
{
double value = Double.parseDouble(rule.getValue().toString());
result = selectUsingDecimal(dm.getRow(rule.getField()), value, rule.getOperator(), dm.getColNames());
}
else
{
String value = rule.getValue().toString();
result = selectUsingText(dm.getRow(rule.getField()), value, rule.getOperator(), dm.getColNames());
}
if (colNames == null)
{
// first queryrule being applied, store results in colnames
colNames = result;
}
else
{
// consecutively: basically, applying an AND operator here
// by removing result from colnames.. so OR not supported
colNames.removeAll(result);
}
if (colNames.size() == 0)
{
throw new Exception("No colnames in resultset, empty matrix!");
}
}
DataMatrixInstance res = dm.getSubMatrix(rowNames, colNames);
return res;
}
/**
* Apply filters (query rules) to the values of a matrix, to get a new
* subset matrix back. The 'field' specifies the column name where the
* filter is applied. And as expected: The 'operator' is the comparator, and
* 'value' is the point of reference.
*
* @param dm
* @param rules
* @return AbstractDataMatrixInstance
* @throws Exception
*/
public static DataMatrixInstance getSubMatrixFilterByColMatrixValues(DataMatrixInstance dm, QueryRule... rules)
throws Exception
{
checkQueryRulesOld(dm, true, rules);
// rowNames is the resultset we want to get
List<String> colNames = dm.getColNames();
List<String> rowNames = null;
// iterate over queryrules
for (QueryRule rule : rules)
{
List<String> result = null;
if (dm.getData().getValueType().equals("Decimal"))
{
double value = Double.parseDouble(rule.getValue().toString());
result = selectUsingDecimal(dm.getCol(rule.getField()), value, rule.getOperator(), dm.getRowNames());
}
else
{
String value = rule.getValue().toString();
result = selectUsingText(dm.getCol(rule.getField()), value, rule.getOperator(), dm.getRowNames());
}
if (rowNames == null)
{
// first queryrule being applied, store results in rownames
rowNames = result;
}
else
{
// consecutively: basically, applying an AND operator here
// by removing result from rownames.. so OR not supported
rowNames.removeAll(result);
}
if (rowNames.size() == 0)
{
throw new Exception("No rownames in resultset, empty matrix!");
}
}
DataMatrixInstance res = dm.getSubMatrix(rowNames, colNames);
return res;
}
public static DataMatrixInstance getSubMatrixFilterByRowEntityValues(DataMatrixInstance dm, Database db,
QueryRule... rules) throws Exception
{
List<String> colNames = dm.getColNames();
// 1. query on row type entities
Query q = db.query(db.getClassForName(dm.getData().getTargetType()));
// 2. entities must be present in row names
q.addRules(new QueryRule("name", Operator.IN, dm.getRowNames()));
// 3. now add other rules
q.addRules(rules);
List<Nameable> subRow = q.find();
List<String> rowNames = new ArrayList<String>();
for (Nameable i : subRow)
{
rowNames.add(i.getName());
}
if (rowNames.size() == 0)
{
throw new Exception("No rownames in resultset, empty matrix!");
}
DataMatrixInstance res = dm.getSubMatrix(rowNames, colNames);
return res;
}
public static DataMatrixInstance getSubMatrixFilterByColEntityValues(DataMatrixInstance dm, Database db,
QueryRule... rules) throws Exception
{
List<String> rowNames = dm.getRowNames();
// 1. query on column type entities
Query q = db.query(db.getClassForName(dm.getData().getFeatureType()));
// 2. entities must be present in column names
q.addRules(new QueryRule("name", Operator.IN, dm.getColNames()));
// 3. now add other rules
q.addRules(rules);
List<Nameable> subCol = q.find();
List<String> colNames = new ArrayList<String>();
for (Nameable i : subCol)
{
colNames.add(i.getName());
}
if (colNames.size() == 0)
{
throw new Exception("No colnames in resultset, empty matrix!");
}
DataMatrixInstance res = dm.getSubMatrix(rowNames, colNames);
return res;
}
public static DataMatrixInstance getSubMatrixByRowValueFilter(DataMatrixInstance matrix, QueryRule... rules)
throws Exception
{
checkQueryRules(rules);
// colNames is the resultset we want to get
List<String> colNames = null;
List<String> rowNames = matrix.getRowNames();
// iterate over queryrules
for (QueryRule rule : rules)
{
List<String> result = null;
if (matrix.getData().getValueType().equals("Decimal"))
{
double value = Double.parseDouble(rule.getValue().toString());
result = selectUsingDecimal(matrix.getRow(Integer.parseInt(rule.getField())), value,
rule.getOperator(), matrix.getColNames());
}
else
{
String value = rule.getValue().toString();
result = selectUsingText(matrix.getRow(Integer.parseInt(rule.getField())), value, rule.getOperator(),
matrix.getColNames());
}
if (colNames == null)
{
// first queryrule being applied, store results in colnames
colNames = result;
}
else
{
// consecutively: basically, applying an AND operator here
// by removing result from colnames.. so OR not supported
colNames.removeAll(result);
}
if (colNames.size() == 0)
{
throw new Exception("No colnames in resultset, empty matrix!");
}
}
DataMatrixInstance res = matrix.getSubMatrix(rowNames, colNames);
return res;
}
public static DataMatrixInstance getSubMatrixFilterByIndex(DataMatrixInstance matrix, QueryRule... rules)
throws Exception
{
checkQueryRules(rules);
if (rules.length != 1)
{
throw new Exception("at the moment supports only 1 QueryRule at a time");
}
String field = rules[0].getField();
Operator op = rules[0].getOperator();
int value = Integer.parseInt(rules[0].getValue().toString());
if (value < 0)
{
throw new Exception("Negative numbers not allowed");
}
if (field.equals("row"))
{
int row = getOffset(op, value);
int nRows = getLimit(matrix.getNumberOfRows(), op, value);
if (nRows < 1)
{
throw new Exception("No rows in resultset, empty matrix!");
}
return matrix.getSubMatrixByOffset(row, nRows, 0, matrix.getNumberOfCols());
}
else if (field.equals("col"))
{
int col = getOffset(op, value);
int nCols = getLimit(matrix.getNumberOfCols(), op, value);
if (nCols < 1)
{
throw new Exception("No cols in resultset, empty matrix!");
}
return matrix.getSubMatrixByOffset(0, matrix.getNumberOfRows(), col, nCols);
}
else
{
throw new Exception("field is not 'row' or 'col'");
}
}
public static int getOffset(Operator op, int value) throws Exception
{
if (op == Operator.EQUALS || op == Operator.GREATER_EQUAL)
{
return value;
}
else if (op == Operator.GREATER)
{
return value + 1;
}
else if (op == Operator.LESS || op == Operator.LESS_EQUAL)
{
return 0;
}
else
{
throw new Exception("unsupported operator " + op);
}
}
public static int getLimit(int numberOfDimElems, Operator op, int value) throws Exception
{
if (op == Operator.LESS_EQUAL)
{
return value;
}
else if (op == Operator.EQUALS)
{
return 1;
}
else if (op == Operator.GREATER)
{
return numberOfDimElems - value - 1;
}
else if (op == Operator.GREATER_EQUAL)
{
return numberOfDimElems - value;
}
else if (op == Operator.LESS)
{
return value - 1;
}
else
{
throw new Exception("unsupported operator " + op);
}
}
// TODO: merge with selectUsingDecimal? or does it have special needs?
public static List<String> selectUsingText(Object[] values, String value, Operator op, List<String> dimNames)
throws Exception
{
List<String> resultNames = new ArrayList<String>();
for (int i = 0; i < values.length; i++)
{
if (values[i] != null)
{
boolean add = false;
if (op == Operator.EQUALS)
{
// using .equals here has the same effect for doubles as ==
if (values[i].toString().equals(value))
{
add = true;
}
}
// attempt to parse to double and compare in non-EQUAL cases!
// for non-numerics: fails with java.lang.NumberFormatException:
// For input string "lalala"
else if (op == Operator.GREATER)
{
if (Double.parseDouble(values[i].toString()) > Double.parseDouble(value))
{
add = true;
}
}
else if (op == Operator.LESS)
{
if (Double.parseDouble(values[i].toString()) < Double.parseDouble(value))
{
add = true;
}
}
else if (op == Operator.GREATER_EQUAL)
{
if (Double.parseDouble(values[i].toString()) >= Double.parseDouble(value))
{
add = true;
}
}
else if (op == Operator.LESS_EQUAL)
{
if (Double.parseDouble(values[i].toString()) <= Double.parseDouble(value))
{
add = true;
}
}
else
{
throw new Exception("Unsupported operation: " + op.toString());
}
if (add == true)
{
resultNames.add(dimNames.get(i));
}
}
}
return resultNames;
}
public static List<String> selectUsingDecimal(Object[] values, double value, Operator op, List<String> dimNames)
throws Exception
{
List<String> resultNames = new ArrayList<String>();
for (int i = 0; i < values.length; i++)
{
if (values[i] != null)
{
boolean add = false;
if (op == Operator.GREATER)
{
if (Double.parseDouble(values[i].toString()) > value)
{
add = true;
}
}
else if (op == Operator.LESS)
{
if (Double.parseDouble(values[i].toString()) < value)
{
add = true;
}
}
else if (op == Operator.GREATER_EQUAL)
{
if (Double.parseDouble(values[i].toString()) >= value)
{
add = true;
}
}
else if (op == Operator.LESS_EQUAL)
{
if (Double.parseDouble(values[i].toString()) <= value)
{
add = true;
}
}
else if (op == Operator.EQUALS)
{
if (Double.parseDouble(values[i].toString()) == value)
{
add = true;
}
}
else
{
throw new Exception("Unsupported operation: " + op.toString());
}
if (add == true)
{
resultNames.add(dimNames.get(i));
}
}
}
return resultNames;
}
private static void checkQueryRules(QueryRule... rules) throws Exception
{
for (QueryRule rule : rules)
{
if (rule.getField() == null)
{
throw new Exception("QueryRule invalid: field is null");
}
if (rule.getValue() == null)
{
throw new Exception("QueryRule invalid: value is null");
}
if (rule.getOperator() == null)
{
throw new Exception("QueryRule invalid: operator is null");
}
}
}
private static void checkQueryRulesOld(DataMatrixInstance dm, boolean appliedOnColumns, QueryRule... rules)
throws Exception
{
for (QueryRule rule : rules)
{
if (rule.getField() == null)
{
throw new Exception("QueryRule invalid: field is null");
}
if (rule.getValue() == null)
{
throw new Exception("QueryRule invalid: value is null");
}
if (rule.getOperator() == null)
{
throw new Exception("QueryRule invalid: operator is null");
}
if (appliedOnColumns && !dm.getColNames().contains(rule.getField()))
{
throw new Exception("QueryRule invalid: no column named '" + rule.getField() + "'");
}
if (!appliedOnColumns && !dm.getRowNames().contains(rule.getField()))
{
throw new Exception("QueryRule invalid: no row named '" + rule.getField() + "'");
}
}
}
public static DataMatrixInstance getSubMatrix2DFilterByRow(DataMatrixInstance matrix, QueryRule... rules)
throws Exception
{
checkQueryRules(rules);
if (rules.length != 1)
{
throw new Exception("at the moment supports only 1 QueryRule at a time");
}
// we want new row ('target') names, get colnames here to reuse
// selectUsingDecimal/selectUsingText
List<String> colNames = matrix.getColNames();
int amount = Integer.parseInt(rules[0].getField());
Operator op = rules[0].getOperator();
Object value = rules[0].getValue();
List<Integer> rowResult = new ArrayList<Integer>();
if (matrix.getData().getValueType().equals("Decimal"))
{
double valueDbl = Double.parseDouble(rules[0].getValue().toString());
for (int row = 0; row < matrix.getNumberOfRows(); row++)
{
List<String> result = selectUsingDecimal(matrix.getRow(row), valueDbl, op, colNames);
if (result.size() >= amount)
{
rowResult.add(row);
}
}
}
else
{
String valueStr = value.toString();
for (int row = 0; row < matrix.getNumberOfRows(); row++)
{
List<String> result = selectUsingText(matrix.getRow(row), valueStr, op, colNames);
if (result.size() >= amount)
{
rowResult.add(row);
}
}
}
if (rowResult.size() == 0)
{
throw new Exception("No rows in resultset, empty matrix!");
}
int[] rowIndices = new int[rowResult.size()];
int[] colIndices = new int[matrix.getNumberOfCols()];
for (int col = 0; col < matrix.getNumberOfCols(); col++)
{
colIndices[col] = col;
}
int rowIndex = 0;
for (int row = 0; row < rowResult.size(); row++)
{
rowIndices[rowIndex] = rowResult.get(row);
rowIndex++;
}
return matrix.getSubMatrix(rowIndices, colIndices);
}
public static DataMatrixInstance getSubMatrix2DFilterByCol(DataMatrixInstance matrix, QueryRule... rules)
throws Exception
{
checkQueryRules(rules);
if (rules.length != 1)
{
throw new Exception("at the moment supports only 1 QueryRule at a time");
}
// we want new col ('feature') names, get rownames here to reuse
// selectUsingDecimal/selectUsingText
List<String> rowNames = matrix.getRowNames();
int amount = Integer.parseInt(rules[0].getField());
Operator op = rules[0].getOperator();
Object value = rules[0].getValue();
List<Integer> colResult = new ArrayList<Integer>();
if (matrix.getData().getValueType().equals("Decimal"))
{
double valueDbl = Double.parseDouble(rules[0].getValue().toString());
for (int col = 0; col < matrix.getNumberOfCols(); col++)
{
List<String> result = selectUsingDecimal(matrix.getCol(col), valueDbl, op, rowNames);
if (result.size() >= amount)
{
colResult.add(col);
}
}
}
else
{
String valueStr = value.toString();
for (int col = 0; col < matrix.getNumberOfCols(); col++)
{
List<String> result = selectUsingText(matrix.getCol(col), valueStr, op, rowNames);
if (result.size() >= amount)
{
colResult.add(col);
}
}
}
if (colResult.size() == 0)
{
throw new Exception("No cols in resultset, empty matrix!");
}
int[] rowIndices = new int[matrix.getNumberOfRows()];
int[] colIndices = new int[colResult.size()];
for (int row = 0; row < matrix.getNumberOfRows(); row++)
{
rowIndices[row] = row;
}
int colIndex = 0;
for (int col = 0; col < colResult.size(); col++)
{
colIndices[colIndex] = colResult.get(col);
colIndex++;
}
return matrix.getSubMatrix(rowIndices, colIndices);
}
}