package matrix.implementations.database; import java.io.File; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import matrix.AbstractDataMatrixInstance; import matrix.implementations.memory.MemoryDataMatrixInstance; import org.molgenis.data.Data; import org.molgenis.data.DecimalDataElement; import org.molgenis.data.TextDataElement; import org.molgenis.framework.db.Database; import org.molgenis.framework.db.QueryRule; import org.molgenis.framework.db.QueryRule.Operator; import org.molgenis.matrix.MatrixException; import org.molgenis.util.Entity; import org.molgenis.util.Tuple; import decorators.NameConvention; public class DatabaseDataMatrixInstance extends AbstractDataMatrixInstance<Object> { private String dataElement = "DataElement"; private String dimensionElement = "DimensionElement"; private String rowString = "Target"; private String rowIndexString = "TargetIndex"; private String colString = "Feature"; private String colIndexString = "FeatureIndex"; private String valueString = "Value"; private String dataString = "Data"; private String decimalTypeString = "Decimal"; private String textTypeString = "Text"; private Database db; private String type; private int matrixId; public void setDatabase(Database db) { this.db = db; } public DatabaseDataMatrixInstance(Database db, Data data) throws Exception { this.db = db; this.type = data.getValueType(); this.matrixId = data.getId().intValue(); this.setData(data); // queryrules to get the right elements: match on data.id, row/col index // = 0, and sort by row/col QueryRule whereData = new QueryRule(dataString, Operator.EQUALS, data.getId()); QueryRule whereRowIndex = new QueryRule(rowIndexString, Operator.EQUALS, "0"); QueryRule orderByColIndex = new QueryRule(Operator.SORTASC, colIndexString); QueryRule whereColIndex = new QueryRule(colIndexString, Operator.EQUALS, "0"); QueryRule orderByRowIndex = new QueryRule(Operator.SORTASC, rowIndexString); // dynamic query on type, eg. Text/Decimal List<? extends Entity> colDataElements = db.find(db.getClassForName(type + "DataElement"), whereData, whereRowIndex, orderByColIndex); List<? extends Entity> rowDataElements = db.find(db.getClassForName(type + "DataElement"), whereData, whereColIndex, orderByRowIndex); // grab the colnames and add to list List<String> colNames = new ArrayList<String>(); for (Entity de : colDataElements) { colNames.add(de.get("feature_name").toString()); } // grab the rownames and add to list List<String> rowNames = new ArrayList<String>(); for (Entity de : rowDataElements) { rowNames.add(de.get("target_name").toString()); } // set row/colnames and number of rows/cols this.setColNames(colNames); this.setNumberOfCols(colNames.size()); this.setRowNames(rowNames); this.setNumberOfRows(rowNames.size()); this.setData(data); } @Override public Object[] getCol(int colIndex) throws Exception { Object[] result = new Object[this.getRowNames().size()]; if (type.equals(decimalTypeString)) { List<DecimalDataElement> dbResult = db.query(DecimalDataElement.class).equals(dataString, matrixId) .equals(colIndexString, colIndex).sortASC(rowIndexString).find(); for (int j = 0; j < dbResult.size(); j++) { result[j] = (Object) dbResult.get(j).getValue(); } } else { List<TextDataElement> dbResult = db.query(TextDataElement.class).equals(dataString, matrixId) .equals(colIndexString, colIndex).sortASC(rowIndexString).find(); for (int j = 0; j < dbResult.size(); j++) { result[j] = (Object) dbResult.get(j).getValue(); } } return result; } @Override public Object getElement(int rowIndex, int colIndex) throws Exception { Object result; if (type.equals(decimalTypeString)) { DecimalDataElement dbResult = db.query(DecimalDataElement.class).equals(dataString, matrixId) .equals(colIndexString, colIndex).equals(rowIndexString, rowIndex).find().get(0); result = (Object) dbResult.getValue(); } else { TextDataElement dbResult = db.query(TextDataElement.class).equals(dataString, matrixId) .equals(colIndexString, colIndex).equals(rowIndexString, rowIndex).find().get(0); result = (Object) dbResult.getValue(); } return result; } @Override public Object[][] getElements() throws MatrixException { try { String maxRowSql = String.format("SELECT MAX(" + rowIndexString + ") AS maxrow FROM " + type + dataElement + " WHERE " + dataString + "=%s", matrixId); List<Tuple> rsList = db.sql(maxRowSql); int maxRow = -1; for (Tuple rs : rsList) { maxRow = rs.getInt("maxrow") + 1; } String maxColSql = String.format("SELECT MAX(" + colIndexString + ") AS maxcol FROM " + type + dataElement + " WHERE " + dataString + "=%s", matrixId); rsList = db.sql(maxColSql); int maxCol = -1; for (Tuple rs : rsList) { maxCol = rs.getInt("maxcol") + 1; } String sql = String.format("SELECT " + rowIndexString + "," + colIndexString + "," + valueString + " FROM " + type + dataElement + " WHERE " + dataString + "=%s", matrixId); rsList = db.sql(sql); Object[][] data = new Object[maxRow][maxCol]; if (type.equals("Decimal")) { for (Tuple rs : rsList) { data[rs.getInt(rowIndexString)][rs.getInt(colIndexString)] = rs.getDouble(valueString); } } else { for (Tuple rs : rsList) { data[rs.getInt(rowIndexString)][rs.getInt(colIndexString)] = rs.getString(valueString); } } return data; } catch (Exception e) { throw new MatrixException(e); } } @Override public Object[] getRow(int rowIndex) throws Exception { Object[] result = new Object[this.getColNames().size()]; if (type.equals(decimalTypeString)) { List<DecimalDataElement> dbResult = db.query(DecimalDataElement.class).equals(dataString, matrixId) .equals(rowIndexString, rowIndex).sortASC(colIndexString).find(); for (int j = 0; j < dbResult.size(); j++) { result[j] = (Object) dbResult.get(j).getValue(); } } else { List<TextDataElement> dbResult = db.query(TextDataElement.class).equals(dataString, matrixId) .equals(rowIndexString, rowIndex).sortASC(colIndexString).find(); for (int j = 0; j < dbResult.size(); j++) { result[j] = (Object) dbResult.get(j).getValue(); } } return result; } @Override public AbstractDataMatrixInstance<Object> getSubMatrix(int[] rowIndices, int[] colIndices) throws MatrixException { try { // the optimized way: find out of indices form a single block // if so, used offset retrieval instead boolean offsetAble = true; for (int i = 0; i < rowIndices.length - 1; i++) { if (rowIndices[i] != (rowIndices[i + 1] + 1)) { offsetAble = false; break; } } if (offsetAble) { for (int i = 0; i < colIndices.length - 1; i++) { if (colIndices[i] != (colIndices[i + 1] + 1)) { offsetAble = false; break; } } } if (offsetAble) { return getSubMatrixByOffset(rowIndices[0], rowIndices.length, colIndices[0], colIndices.length); } // regular way of retrieval, not too bad for database matrix (single // query) HashMap<Integer, Integer> rowIndexPositions = new HashMap<Integer, Integer>(); HashMap<Integer, Integer> colIndexPositions = new HashMap<Integer, Integer>(); Integer[] rowIndicesCastable = new Integer[rowIndices.length]; Integer[] colIndicesCastable = new Integer[colIndices.length]; for (int i = 0; i < rowIndices.length; i++) { rowIndexPositions.put(rowIndices[i], i); rowIndicesCastable[i] = rowIndices[i]; } for (int i = 0; i < colIndices.length; i++) { colIndexPositions.put(colIndices[i], i); colIndicesCastable[i] = colIndices[i]; } String sql = "SELECT " + rowIndexString + ", " + colIndexString + ", " + valueString + " FROM " + type + dataElement + ""; List<Tuple> rsList = db.sql(sql, new QueryRule(rowIndexString, Operator.IN, rowIndicesCastable), new QueryRule(colIndexString, Operator.IN, colIndicesCastable), new QueryRule(dataString, Operator.EQUALS, matrixId)); Object[][] data = new Object[rowIndices.length][colIndices.length]; if (type.equals(decimalTypeString)) { for (Tuple rs : rsList) { data[rowIndexPositions.get(rs.getInt(rowIndexString))][colIndexPositions.get(rs .getInt(colIndexString))] = rs.getDouble(valueString); } } else { for (Tuple rs : rsList) { data[rowIndexPositions.get(rs.getInt(rowIndexString))][colIndexPositions.get(rs .getInt(colIndexString))] = rs.getString(valueString); } } List<String> rowNames = new ArrayList<String>(); List<String> colNames = new ArrayList<String>(); for (int rowIndex : rowIndices) { rowNames.add(this.getRowNames().get(rowIndex).toString()); } for (int colIndex : colIndices) { colNames.add(this.getColNames().get(colIndex).toString()); } return new MemoryDataMatrixInstance<Object>(rowNames, colNames, data, this.getData()); } catch (Exception e) { throw new MatrixException(e); } } @Override public AbstractDataMatrixInstance<Object> getSubMatrixByOffset(int row, int rows, int col, int cols) throws Exception { String sql = String.format("SELECT " + rowIndexString + "," + colIndexString + "," + valueString + " FROM " + type + dataElement + " WHERE " + rowIndexString + ">=%s AND " + rowIndexString + "<%s AND " + colIndexString + ">=%s AND " + colIndexString + "<%s AND " + dataString + "=%s", row, row + rows, col, col + cols, matrixId); List<Tuple> rsList = db.sql(sql); Object[][] data = new Object[rows][cols]; if (type.equals(decimalTypeString)) { for (Tuple rs : rsList) { data[rs.getInt(rowIndexString) - row][rs.getInt(colIndexString) - col] = rs.getDouble(valueString); } } else { for (Tuple rs : rsList) { data[rs.getInt(rowIndexString) - row][rs.getInt(colIndexString) - col] = rs.getString(valueString); } } return new MemoryDataMatrixInstance<Object>(this.getRowNames().subList(row, row + rows), this.getColNames() .subList(col, col + cols), data, this.getData()); } @Override public File getAsFile() throws Exception { File tmp = new File(System.getProperty("java.io.tmpdir") + File.separator + NameConvention.escapeFileName(this.getData().getInvestigation_Name()) + "_" + NameConvention.escapeFileName(this.getData().getName()) + ".txt"); if (tmp.exists()) { boolean deleteSuccess = tmp.delete(); if (!deleteSuccess) { throw new Exception("Deletion of tmp file " + tmp.getAbsolutePath() + " failed."); } } boolean createTmp = tmp.createNewFile(); if (!createTmp) { throw new Exception("Creation of tmp file " + tmp.getAbsolutePath() + " failed."); } PrintWriter out = new PrintWriter(tmp); this.writeToCsvWriter(out); out.close(); // FIXME: close 'out'? return tmp; } @Override public void addColumn() throws Exception { throw new Exception("Action not possible"); } @Override public void addRow() throws Exception { throw new Exception("Action not possible"); } @Override public void updateElement() throws Exception { throw new Exception("Action not possible"); } }