package org.molgenis.matrix; import java.io.StringWriter; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.molgenis.framework.db.Database; import org.molgenis.framework.db.DatabaseException; import org.molgenis.framework.db.Query; import org.molgenis.pheno.ObservationElement; import org.molgenis.pheno.ObservedValue; import org.molgenis.util.CsvStringWriter; import org.molgenis.util.Entity; /** * Simple memory based matrix of ObservedValue */ public class PhenoMemoryMatrix<E extends ObservationElement, A extends ObservationElement> extends MemoryMatrix<E, A, ObservedValue> { Class<E> rowType; Class<A> colType; public PhenoMemoryMatrix(Class<E> rowType, Class<A> colType, Database db) throws MatrixException, DatabaseException, ParseException { this(db.find(rowType), db.find(colType), db); this.rowType = rowType; this.colType = colType; } /** Construct a pheno matrix from database, only using selected rows/cols */ public PhenoMemoryMatrix(List<E> rows, List<A> cols, Database db) throws MatrixException, DatabaseException, ParseException { // names are unique in whole database??? super(rows, cols, ObservedValue.class); // get rowType and colType for (E row : rows) if (row == null) throw new MatrixException("rows may not have null values"); for (A col : cols) if (col == null) throw new MatrixException("cols may not have null values"); this.rowType = (Class<E>) rows.get(0).getClass(); this.colType = (Class<A>) cols.get(0).getClass(); // get a map of index->id List<Object> rowIds = getIds(rows); List<Object> colIds = getIds(cols); // retrieve observed values for this matrix, on duplicate, sorted by // timestamp Query<ObservedValue> q = db.query(ObservedValue.class); q.in(ObservedValue.FEATURE, colIds); q.in(ObservedValue.TARGET, rowIds); q.sortASC(ObservedValue.TIME); List<ObservedValue> values = q.find(); // map values to right position, sorting by timestamp ensures we have // most recent value for each row,col pair for (ObservedValue v : values) { this.setValue(rowIds.indexOf(v.getTarget()), colIds.indexOf(v.getFeature()), v); } } /** * Construct a pheno matrix from a StringMatrix (such as StringCsvMatrix) * * @throws IllegalAccessException * @throws InstantiationException * @throws MatrixException */ public PhenoMemoryMatrix(Class<E> rowType, Class<A> colType, Matrix<String, String, String> matrix) throws MatrixException { super(); this.rowType = rowType; this.colType = colType; try { // create new rowType instances List<E> rows = new ArrayList<E>(); for (String rowName : matrix.getRowNames()) { E rowEntity = rowType.newInstance(); rowEntity.setName(rowName); rows.add(rowEntity); } this.setRowNames(rows); // create new colType instances List<A> cols = new ArrayList<A>(); for (String colName : matrix.getColNames()) { A colEntity = colType.newInstance(); colEntity.setName(colName); cols.add(colEntity); } this.setColNames(cols); // create new ObservedValue instances ObservedValue[][] values = this.create(rows.size(), cols.size(), ObservedValue.class); for (int i = 0; i < rows.size(); i++) { for (int j = 0; j < cols.size(); j++) { ObservedValue v = new ObservedValue(); v.setTarget_Name(rows.get(i).getName()); // v.setTarget___Type(this.rowType.getSimpleName()); v.setFeature_Name(cols.get(j).getName()); // v.setFeature___Type(this.colType.getSimpleName()); v.setValue(matrix.getValue(i, j)); values[i][j] = v; } } this.setValues(values); } catch (Exception e) { throw new MatrixException(e); } } private static List<Object> getIds(List<? extends Entity> entities) { List<Object> result = new ArrayList<Object>(); for (Entity e : entities) result.add(e.getIdValue()); return result; } /** * Write this matrix to the database. Use 'action' parameter to specify * behaviour in case of duplicates. * * @param db * @param action * @throws DatabaseException */ public void store(Database db, Database.DatabaseAction action) throws DatabaseException { boolean inTransaction = false; try { if (!db.inTx()) { db.beginTx(); inTransaction = true; } // except in case of remove, first add the targets. if (!action.equals(Database.DatabaseAction.REMOVE) && !action.equals(Database.DatabaseAction.REMOVE_IGNORE_MISSING)) { db.update(this.getRowNames(), action, ObservationElement.NAME); db.update(this.getColNames(), action, ObservationElement.NAME); } for (int i = 0; i < this.getRowCount(); i++) { db.update(Arrays.asList(this.getRow(i)), action, ObservedValue.FEATURE_NAME, ObservedValue.TARGET_NAME, ObservedValue.VALUE, ObservedValue.TIME); } // remove unless linked to another value. BIG FIXME!!! if (action.equals(Database.DatabaseAction.REMOVE) || action.equals(Database.DatabaseAction.REMOVE_IGNORE_MISSING)) { db.update(this.getRowNames(), action, ObservationElement.NAME); db.update(this.getColNames(), action, ObservationElement.NAME); } if (inTransaction) db.commitTx(); } catch (Exception e) { if (inTransaction) db.rollbackTx(); throw new DatabaseException(e); } } public void store(Database db) throws DatabaseException { store(db, Database.DatabaseAction.ADD_IGNORE_EXISTING); } public String toString() { StringWriter string = new StringWriter(); try { CsvStringWriter writer = new CsvStringWriter(string); // print headers boolean first = true; for (A col : getColNames()) { // take care of separator if (!first) writer.writeSeparator(); else first = false; // write a header value writer.writeValue(col.getName()); } writer.writeEndOfLine(); for (E row : getRowNames()) { writer.writeValue(row.getName()); for (A col : getColNames()) { writer.writeSeparator(); if (this.getValue(row, col) != null) writer.writeValue(this.getValue(row, col).getValue()); else writer.writeValue(writer.getMissingValue()); } writer.writeEndOfLine(); } } catch (Exception e) { e.printStackTrace(); return "ERROR"; } return string.toString(); } }