/* *------------------------------------------------------------------------------ * Copyright (C) 2006-2015 University of Dundee. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *------------------------------------------------------------------------------ */ package org.openmicroscopy.shoola.agents.dataBrowser.view; import java.awt.Color; import java.awt.Dimension; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.collections.CollectionUtils; import org.openmicroscopy.shoola.agents.dataBrowser.DataBrowserAgent; import org.openmicroscopy.shoola.agents.dataBrowser.DataBrowserLoader; import org.openmicroscopy.shoola.agents.dataBrowser.DataBrowserTranslator; import org.openmicroscopy.shoola.agents.dataBrowser.PlateSaver; import org.openmicroscopy.shoola.agents.dataBrowser.ThumbnailFieldsLoader; import org.openmicroscopy.shoola.agents.dataBrowser.ThumbnailProvider; import org.openmicroscopy.shoola.agents.dataBrowser.browser.BrowserFactory; import org.openmicroscopy.shoola.agents.dataBrowser.browser.CellDisplay; import org.openmicroscopy.shoola.agents.dataBrowser.browser.ImageDisplay; import org.openmicroscopy.shoola.agents.dataBrowser.browser.ImageNode; import org.openmicroscopy.shoola.agents.dataBrowser.browser.ImageSet; import org.openmicroscopy.shoola.agents.dataBrowser.browser.Thumbnail; import org.openmicroscopy.shoola.agents.dataBrowser.browser.WellImageSet; import org.openmicroscopy.shoola.agents.dataBrowser.browser.WellSampleNode; import org.openmicroscopy.shoola.agents.dataBrowser.layout.LayoutFactory; import org.openmicroscopy.shoola.agents.dataBrowser.visitor.DecoratorVisitor; import omero.gateway.model.TableResult; import omero.gateway.SecurityContext; import org.openmicroscopy.shoola.util.image.geom.Factory; import org.openmicroscopy.shoola.util.ui.PlateGrid; import org.openmicroscopy.shoola.util.ui.UIUtilities; import org.openmicroscopy.shoola.util.ui.WellGridElement; import org.openmicroscopy.shoola.util.ui.colourpicker.ColourObject; import omero.gateway.model.DataObject; import omero.gateway.model.ImageData; import omero.gateway.model.PlateData; import omero.gateway.model.WellData; import omero.gateway.model.WellSampleData; /** * A concrete model for a plate. * * @author Jean-Marie Burel      * <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @author Donald MacDonald      * <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a> * @version 3.0 * <small> * (<b>Internal version:</b> $Revision: $Date: $) * </small> * @since 3.0-Beta3 */ class WellsModel extends DataBrowserModel { /** The number of rows. */ private int rows; /** The number of columns. */ private int columns; /** The dimension of a well. */ private Dimension wellDimension; /** The collection of nodes hosting the wells. */ private List<ImageDisplay> wellNodes; /** The collection of nodes used to display cells e.g. A-1. */ private Set<CellDisplay> cells; /** The number of fields per well. */ private int fieldsNumber; /** The selected field. */ private int selectedField; /** Indicates how to display a row. */ private int rowSequenceIndex; /** Indicates how to display a column. */ private int columnSequenceIndex; /** Value indicating if the wells are valid or not. */ private List<WellGridElement> validWells; /** Flag indicating to load or not the thumbnails. */ private boolean withThumbnails; /** The selected nodes. */ private List<WellImageSet> selectedNodes; /** * Sorts the passed nodes by row. * * @param nodes The nodes to sort. * @return See above. */ private List<ImageDisplay> sortByRow(Set nodes) { List<ImageDisplay> l = new ArrayList<ImageDisplay>(); if (nodes == null) return l; Iterator i = nodes.iterator(); while (i.hasNext()) { l.add((ImageDisplay) i.next()); } Comparator c = new Comparator() { public int compare(Object o1, Object o2) { WellData w1 = (WellData) ((WellImageSet) o1).getHierarchyObject(), w2 = (WellData) ((WellImageSet) o2).getHierarchyObject(); int n1 = w1.getRow(); int n2 = w2.getRow(); int v = 0; if (n1 < n2) v = -1; else if (n1 > n2) v = 1; else if (n1 == n2) { int c1 = w1.getColumn(); int c2 = w2.getColumn(); if (c1 < c2) v = -1; else if (c1 > c2) v = 1; } return v; } }; Collections.sort(l, c); return l; } /** * Creates the color related to the passed Well. * * @param data The well to handle. * @return See above. */ private Color createColor(WellData data) { int red = data.getRed(); int green = data.getGreen(); int blue = data.getBlue(); int alpha = data.getAlpha(); if (red < 0 || green < 0 || blue < 0 || alpha < 0) return null; if (red > 255 || green > 255 || blue > 255 || alpha > 255) return null; return new Color(red, green, blue, alpha); } /** * Returns <code>true</code> if the passed description are the same, * <code>false</code> otherwise. * * @param d1 The color to handle. * @param d2 The color to handle. * @return See above. */ private boolean isSameDescription(String d1, String d2) { if (d1 == null && d2 == null) return true; if (d1 == null && d2 != null) return false; if (d1 != null && d2 == null) return false; if (d1 != null && d2 != null) { String t1 = d1.trim(); String t2 = d2.trim(); if (t1 != null && t2 != null) return t1.equals(t2); return false; } return false; } /** * Handles the selection of a cell * * @param cell The selected cell. * @param well The well to handle. * @param results The collection of objects to update. */ private void handleCellSelection(CellDisplay cell, WellImageSet well, List<DataObject> results) { String description = cell.getDescription(); Color c = cell.getHighlight(); WellData data = (WellData) well.getHierarchyObject(); data.setWellType(description); well.setDescription(description); results.add(data); if (c == null || !cell.isSpecified()) { data.setRed(null); } else { data.setRed(c.getRed()); data.setGreen(c.getGreen()); data.setBlue(c.getBlue()); data.setAlpha(c.getAlpha()); } well.setHighlight(c); } /** * Creates a new instance. * * @param ctx The security context. * @param parent The parent of the wells. * @param wells The collection to wells the model is for. * @param withThumbnails Pass <code>true</code> to load the thumbnails, * <code>false</code> otherwise. */ WellsModel(SecurityContext ctx, Object parent, Set<WellData> wells, boolean withThumbnails) { super(ctx); if (wells == null) throw new IllegalArgumentException("No wells."); this.withThumbnails = withThumbnails; selectedNodes = new ArrayList<WellImageSet>(); wellDimension = null; this.parent = parent; wellNodes = sortByRow(DataBrowserTranslator.transformHierarchy(wells)); PlateData plate = (PlateData) parent; columnSequenceIndex = plate.getColumnSequenceIndex(); rowSequenceIndex = plate.getRowSequenceIndex(); selectedField = plate.getDefaultSample(); if (selectedField < 0) selectedField = 0; Set<ImageDisplay> samples = new HashSet<ImageDisplay>(); cells = new HashSet<CellDisplay>(); rows = -1; columns = -1; int row, column; Iterator<ImageDisplay> j = wellNodes.iterator(); WellImageSet node; ImageNode selected; int f; String columnSequence; String rowSequence; Map<Integer, ColourObject> cMap = new HashMap<Integer, ColourObject>(); Map<Integer, ColourObject> rMap = new HashMap<Integer, ColourObject>(); WellData data; String type; ColourObject co; Color color; boolean b; validWells = new ArrayList<WellGridElement>(); int minRow = -1; int minColumn = -1; while (j.hasNext()) { node = (WellImageSet) j.next(); row = node.getRow(); column = node.getColumn(); data = (WellData) node.getHierarchyObject(); type = data.getWellType(); if (cMap.containsKey(column)) { co = cMap.get(column); color = createColor(data); if (!UIUtilities.isSameColors(co.getColor(), color, true) || !isSameDescription(co.getDescription(), type)) { co.setColor(null); co.setDescription(null); cMap.put(column, co); } } else { cMap.put(column, new ColourObject(createColor(data), type)); } if (rMap.containsKey(row)) { co = rMap.get(row); color = createColor(data); if (!UIUtilities.isSameColors(co.getColor(), color, true) || !isSameDescription(co.getDescription(), type)) { co.setColor(null); co.setDescription(null); rMap.put(row, co); } } else { rMap.put(row, new ColourObject(createColor(data), type)); } if (row > rows) rows = row; if (column > columns) columns = column; if (minRow < 0 || minRow > row) { minRow = row; } if (minColumn < 0 || minColumn > column) { minColumn = column; } columnSequence = ""; if (columnSequenceIndex == PlateData.ASCENDING_LETTER) columnSequence = UIUtilities.LETTERS.get(column+1); else if (columnSequenceIndex == PlateData.ASCENDING_NUMBER) columnSequence = ""+(column+1); rowSequence = ""; if (rowSequenceIndex == PlateData.ASCENDING_LETTER) rowSequence = UIUtilities.LETTERS.get(row+1); else if (rowSequenceIndex == PlateData.ASCENDING_NUMBER) rowSequence = ""+(row+1); node.setCellDisplay(columnSequence, rowSequence); f = node.getNumberOfSamples(); if (fieldsNumber < f) fieldsNumber = f; node.setSelectedWellSample(selectedField); selected = node.getSelectedWellSample(); //set the title to Row/Column node.formatWellSampleTitle(); samples.add(selected); b = false; if (node.isSampleValid()) { wellDimension = selected.getThumbnail().getOriginalSize(); b = true; } validWells.add(new WellGridElement(row, column, b)); } // if (minRow >= 0 || minColumn >= 0) { j = wellNodes.iterator(); while (j.hasNext()) { node = (WellImageSet) j.next(); if (minRow > 0) node.setIndentRow(minRow); if (minColumn > 0) node.setIndentColumn(minColumn); if (node.getRow() == minRow || node.getColumn() == minColumn) node.formatWellSampleTitle(); } } columns++; rows++; CellDisplay cell; for (int k = 1; k <= columns; k++) { columnSequence = ""; if (columnSequenceIndex == PlateData.ASCENDING_LETTER) columnSequence = UIUtilities.LETTERS.get(k+1); else if (columnSequenceIndex == PlateData.ASCENDING_NUMBER) columnSequence = ""+k; cell = new CellDisplay(k-1, columnSequence); co = cMap.get(k-1); if (co != null) { cell.setHighlight(co.getColor()); cell.setDescription(co.getDescription()); } //if (!isMac) //samples.add(cell); //cells.add(cell); } for (int k = 1; k <= rows; k++) { rowSequence = ""; if (rowSequenceIndex == PlateData.ASCENDING_LETTER) rowSequence = UIUtilities.LETTERS.get(k); else if (rowSequenceIndex == PlateData.ASCENDING_NUMBER) rowSequence = ""+k; cell = new CellDisplay(k-1, rowSequence, CellDisplay.TYPE_VERTICAL); co = rMap.get(k-1); if (co != null) { cell.setHighlight(co.getColor()); cell.setDescription(co.getDescription()); } //if (!isMac) //samples.add(cell); //cells.add(cell); } browser = BrowserFactory.createBrowser(samples); browser.accept(new DecoratorVisitor(getCurrentUser().getId())); layoutBrowser(LayoutFactory.PLATE_LAYOUT); if (wellDimension == null) wellDimension = new Dimension(ThumbnailProvider.THUMB_MAX_WIDTH, ThumbnailProvider.THUMB_MAX_HEIGHT); } /** * Indicates how to display a column. * * @return See above. */ int getColumnSequenceIndex() { if (columnSequenceIndex == PlateData.ASCENDING_LETTER) return PlateGrid.ASCENDING_LETTER; return PlateGrid.ASCENDING_NUMBER; } /** * Indicates how to display a row. * * @return See above. */ int getRowSequenceIndex() { if (rowSequenceIndex == PlateData.ASCENDING_LETTER) return PlateGrid.ASCENDING_LETTER; return PlateGrid.ASCENDING_NUMBER; } /** * Returns an array indicating the valid wells. * * @return See above. */ List<WellGridElement> getValidWells() { return validWells; } /** * Returns the number of fields per well. * * @return See above. */ int getFieldsNumber() { return fieldsNumber; } /** * Returns the selected field, the default value is <code>0</code>. * * @return See above. */ int getSelectedField() { return selectedField; } /** * Sets the selected well. This should only be needed for the fields * view. * * @param node The selected node. */ void setSelectedWell(WellImageSet node) { if (selectedNodes != null) selectedNodes.clear(); List<WellImageSet> l = new ArrayList<WellImageSet>(1); l.add(node); setSelectedWells(l); } /** * Sets the selected wells. This should only be needed for the fields * view. * * @param node The selected node. */ void setSelectedWells(List<WellImageSet> nodes) { if (nodes == null) selectedNodes.clear(); else selectedNodes = nodes; } /** * Returns the selected well. * * @return See above. */ WellImageSet getSelectedWell() { if (selectedNodes == null || selectedNodes.size() == 0) return null; return selectedNodes.get(0); } /** * Returns the collection of selected wells. * * @return See above. */ List<WellImageSet> getSelectedWells() { return selectedNodes; } /** * Views the selected field. * * @param index The index of the field to view. */ void viewField(int index) { if (index < 0 || index >= fieldsNumber) return; selectedField = index; Set<ImageDisplay> samples = new HashSet<ImageDisplay>(); List<ImageDisplay> l = getNodes(); Iterator<ImageDisplay> i = l.iterator(); WellImageSet well; int row = -1; int col = -1; Collection<ImageDisplay> c = browser.getSelectedDisplays(); Map<Integer, Integer> location = new HashMap<Integer, Integer>(); WellSampleNode selected; if (c != null && c.size() > 0) { Iterator<ImageDisplay> j = c.iterator(); Object object; while (j.hasNext()) { object = j.next(); if (object instanceof WellSampleNode) { selected = (WellSampleNode) object; location.put(selected.getRow(), selected.getColumn()); } } } List<ImageDisplay> nodes = new ArrayList<ImageDisplay>(); while (i.hasNext()) { well = (WellImageSet) i.next(); well.setSelectedWellSample(index); selected = (WellSampleNode) well.getSelectedWellSample(); row = selected.getRow(); if (location.containsKey(row)) { col = location.get(row); if (selected.getColumn() == col) nodes.add(selected); } samples.add(selected); } samples.addAll(cells); browser.refresh(samples, nodes); layoutBrowser(LayoutFactory.PLATE_LAYOUT); //quietly save the field. PlateData plate = (PlateData) parent; long userID = DataBrowserAgent.getUserDetails().getId(); if (plate.getOwner().getId() == userID) { plate.setDefaultSample(selectedField); List<DataObject> list = new ArrayList<DataObject>(); list.add(plate); DataBrowserLoader loader = new PlateSaver(component, ctx, list); loader.load(); } } /** * Sets the tabular data. * * @param data The value to set. */ void setTabularData(List<TableResult> data) { List<ImageDisplay> nodes = getNodes(); if (nodes == null || nodes.size() == 0) return; Iterator<ImageDisplay> i = nodes.iterator(); WellImageSet well; while (i.hasNext()) { well = (WellImageSet) i.next(); well.setTabularData(data); } } /** * Sets the values for the row or the column. * Returns the collection of wells to update. * * @param cell The selected cell. */ void setSelectedCell(CellDisplay cell) { if (cell == null) return; List<DataObject> results = new ArrayList<DataObject >(); List<ImageDisplay> l = getNodes(); Iterator<ImageDisplay> i = l.iterator(); WellImageSet well; int index = cell.getIndex(); if (cell.getType() == CellDisplay.TYPE_HORIZONTAL) { while (i.hasNext()) { well = (WellImageSet) i.next(); if (well.getColumn() == index) { handleCellSelection(cell, well, results); } } } else { while (i.hasNext()) { well = (WellImageSet) i.next(); if (well.getRow() == index) { handleCellSelection(cell, well, results); } } } if (results.size() > 0) { DataBrowserLoader loader = new PlateSaver(component, ctx, results); loader.load(); } } /** * Sets the passed node as the current node. * * @param node See above. */ void setSelectedField(WellSampleNode node) { browser.setSelectedDisplay(node, false, true); } /** * Returns the number of rows. * * @return See above. */ int getRows() { return rows; } /** * Returns the number of columns. * * @return See above. */ int getColumns() { return columns; } /** * Returns <code>true</code> is the selected well corresponds to the passed * one, <code>false</code> otherwise. * * @param row The row identifying the well. * @param column The column identifying the well. * @return See above. */ boolean isSameWell(int row, int column) { WellImageSet selectedNode = getSelectedWell(); if (selectedNode == null) return false; return (selectedNode.getRow() == row && selectedNode.getColumn() == column); } /** * Returns the well corresponding to the passed location. * * @param row The row identifying the well. * @param column The column identifying the well. * @return See above. */ WellImageSet getWell(int row, int column) { List<ImageDisplay> l = getNodes(); Iterator<ImageDisplay> i = l.iterator(); WellImageSet well; while (i.hasNext()) { well = (WellImageSet) i.next(); if (well.getColumn() == column && well.getRow() == row) return well; } return null; } /** * Creates a concrete loader. * * @param row The row identifying the well. * @param column The column identifying the well. * @return See above. */ DataBrowserLoader createFieldsLoader(int row, int column) { List<ImageDisplay> l = getNodes(); Iterator<ImageDisplay> i = l.iterator(); ImageSet node; List<DataObject> images = new ArrayList<DataObject>(); WellSampleData data; Thumbnail thumb; WellImageSet wis; List<WellSampleNode> nodes; Iterator<WellSampleNode> j; WellSampleNode n; if (selectedNodes != null) selectedNodes.clear(); while (i.hasNext()) { node = (ImageSet) i.next(); if (node instanceof WellImageSet) { wis = (WellImageSet) node; if (wis.getRow() == row && wis.getColumn() == column) { setSelectedWell(wis); nodes = wis.getWellSamples(); j = nodes.iterator(); while (j.hasNext()) { n = j.next(); data = (WellSampleData) n.getHierarchyObject(); if (data.getId() < 0) { thumb = n.getThumbnail(); thumb.setValid(false); thumb.setFullScaleThumb( Factory.createDefaultImageThumbnail( wellDimension.width, wellDimension.height)); } else images.add(data.getImage()); } } } } if (images.size() == 0) return null; return new ThumbnailFieldsLoader(component, ctx, images, row, column); } /** * Creates a concrete loader. * @see DataBrowserModel#createDataLoader(boolean, Collection) */ protected List<DataBrowserLoader> createDataLoader(boolean refresh, Collection ids) { if (!withThumbnails) return null; List<ImageDisplay> l = getNodes(); Iterator<ImageDisplay> i = l.iterator(); ImageSet node; List<DataObject> images = new ArrayList<DataObject>(); ImageNode selected; WellSampleData data; Thumbnail thumb; while (i.hasNext()) { node = (ImageSet) i.next(); if (node instanceof WellImageSet) { selected = ((WellImageSet) node).getSelectedWellSample(); data = (WellSampleData) selected.getHierarchyObject(); if (data.getId() < 0) { thumb = selected.getThumbnail(); thumb.setValid(false); thumb.setFullScaleThumb( Factory.createDefaultImageThumbnail( wellDimension.width, wellDimension.height)); } else { ImageData img = data.getImage(); if(CollectionUtils.isEmpty(ids) || ids.contains(img.getId())) images.add(data.getImage()); } } } if (images.size() == 0) return null; return createThumbnailsLoader(sorter.sort(images)); } /** * Returns the type of this model. * @see DataBrowserModel#getType() */ protected int getType() { return DataBrowserModel.WELLS; } /** * No-operation implementation in our case. * @see DataBrowserModel#getNodes() */ protected List<ImageDisplay> getNodes() { return wellNodes; } }