/*
*------------------------------------------------------------------------------
* Copyright (C) 2006-2016 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.measurement.view;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Map.Entry;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.filechooser.FileFilter;
import org.apache.commons.collections.CollectionUtils;
import org.jhotdraw.draw.AttributeKey;
import org.jhotdraw.draw.Drawing;
import org.jhotdraw.draw.Figure;
import org.openmicroscopy.shoola.agents.events.measurement.MeasurementToolLoaded;
import org.openmicroscopy.shoola.agents.events.measurement.ROIEvent;
import org.openmicroscopy.shoola.agents.measurement.MeasurementAgent;
import org.openmicroscopy.shoola.agents.measurement.util.FileMap;
import org.openmicroscopy.shoola.agents.util.EditorUtil;
import org.openmicroscopy.shoola.agents.util.SelectionWizard;
import org.openmicroscopy.shoola.env.config.Registry;
import org.openmicroscopy.shoola.env.data.model.DeletableObject;
import omero.gateway.SecurityContext;
import omero.gateway.model.ROIResult;
import org.openmicroscopy.shoola.env.data.util.StructuredDataResults;
import org.openmicroscopy.shoola.env.event.EventBus;
import omero.log.LogMessage;
import omero.log.Logger;
import org.openmicroscopy.shoola.env.ui.UserNotifier;
import org.openmicroscopy.shoola.util.ui.UIUtilities;
import org.openmicroscopy.shoola.util.ui.component.AbstractComponent;
import org.openmicroscopy.shoola.util.ui.filechooser.FileChooser;
import org.openmicroscopy.shoola.util.filter.file.XMLFilter;
import org.openmicroscopy.shoola.util.roi.exception.NoSuchROIException;
import org.openmicroscopy.shoola.util.roi.exception.ParsingException;
import org.openmicroscopy.shoola.util.roi.figures.MeasureTextFigure;
import org.openmicroscopy.shoola.util.roi.figures.ROIFigure;
import org.openmicroscopy.shoola.util.roi.model.ROI;
import org.openmicroscopy.shoola.util.roi.model.ROIShape;
import org.openmicroscopy.shoola.util.roi.model.ShapeList;
import org.openmicroscopy.shoola.util.roi.model.annotation.AnnotationKeys;
import org.openmicroscopy.shoola.util.roi.model.util.Coord3D;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import omero.gateway.model.AnnotationData;
import omero.gateway.model.ChannelData;
import omero.gateway.model.DataObject;
import omero.gateway.model.ExperimenterData;
import omero.gateway.model.FileAnnotationData;
import omero.gateway.model.ROIData;
import omero.gateway.model.ShapeData;
import omero.gateway.model.TagAnnotationData;
/**
* Implements the {@link MeasurementViewer} interface to provide the
* functionality required of the Measurement viewer component.
* This class is the component hub and embeds the component's MVC triad.
* It manages the component's state machine and fires state change
* notifications as appropriate, but delegates actual functionality to the
* MVC sub-components.
*
* @see org.openmicroscopy.shoola.agents.measurement.view.MeasurementViewerModel
* @see org.openmicroscopy.shoola.agents.measurement.view.MeasurementViewerUI
* @see org.openmicroscopy.shoola.agents.measurement.view.MeasurementViewerControl
*
* @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
* @since OME3.0
*/
class MeasurementViewerComponent
extends AbstractComponent
implements MeasurementViewer
{
/** The Model sub-component. */
private MeasurementViewerModel model;
/** The Control sub-component. */
private MeasurementViewerControl controller;
/** The View sub-component. */
private MeasurementViewerUI view;
/**
* Posts an event to indicating to add or remove the component
* from the display.
*
* @param index Either {@link MeasurementToolLoaded#ADD} or
* {@link MeasurementToolLoaded#REMOVE}.
*/
private void postEvent(int index)
{
MeasurementToolLoaded response =
new MeasurementToolLoaded(
MeasurementViewerFactory.getRequest(model.getPixelsID()),
model.getSecurityContext(),
model.getDrawingView(), index);
EventBus bus = MeasurementAgent.getRegistry().getEventBus();
bus.post(response);
}
/**
* Creates a file chooser corresponding to the passed type.
*
* @param type The type of the file chooser.
* @return See above.
*/
private FileChooser createChooserDialog(int type)
{
String word = "Save ";
if (type == FileChooser.LOAD) word = "Load ";
String title = word+"the ROI File";
String text = word+"the ROI data in the file associate with the image.";
List<FileFilter> filters = new ArrayList<FileFilter>();
filters.add(new XMLFilter());
FileChooser chooser = new FileChooser(view, type, title, text, filters,
false, true);
try {
File f = UIUtilities.getDefaultFolder();
if (f != null) chooser.setCurrentDirectory(f);
} catch (Exception ex) {}
try
{
String s = FileMap.getSavedFile(model.getServerName(),
model.getUserName(), model.getPixelsID());
File savedFile;
if (s != null) {
savedFile = new File(s);
chooser.setCurrentDirectory(savedFile);
chooser.setSelectedFile(savedFile);
} else {
if (type == FileChooser.SAVE) {
s = model.getImageName();
savedFile = new File(s);
chooser.setSelectedFile(savedFile.getName());
}
}
} catch (ParsingException e) {
// Do nothing as we're really only looking to see if the default
// directory or filename should be set for loading.
}
return chooser;
}
/**
* Creates a new instance.
* The {@link #initialize() initialize} method should be called straigh
* after to complete the MVC set up.
*
* @param model The Model sub-component. Mustn't be <code>null</code>.
*/
MeasurementViewerComponent(MeasurementViewerModel model)
{
if (model == null) throw new NullPointerException("No model.");
this.model = model;
controller = new MeasurementViewerControl();
view = new MeasurementViewerUI(model.getImageTitle());
}
/** Links up the MVC triad. */
void initialize()
{
model.initialize(this);
controller.initialize(this, view);
view.initialize(this, controller, model);
}
/**
* Returns the Model sub-component.
*
* @return See above.
*/
MeasurementViewerModel getModel() { return model; }
/** Saves the ROI (not asynchronously) and discards. */
void saveAndDiscard()
{
model.saveROIToServer(false, false);
discard();
}
/**
* Invokes when the ROI has been deleted.
*
* @param imageID The image's identifier.
*/
void onROIDeleted(long imageID)
{
model.onROIDeleted(imageID);
fireStateChange();
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#activate()
*/
public void activate()
{
activate(model.getMeasurements(), model.isHCSData(),
model.isBigImage());
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#activate(List, boolean, boolean)
*/
public void activate(List<FileAnnotationData> measurements, boolean HCSData,
boolean isBigImage)
{
int state = model.getState();
switch (state) {
case NEW:
//Sets the dimension of the drawing canvas;
double f = model.getMagnification();
Dimension d = new Dimension((int) (model.getSizeX()*f),
(int) (model.getSizeY()*f));
UIUtilities.setDefaultSize(model.getDrawingView(), d);
model.getDrawingView().setSize(d);
model.setHCSData(HCSData);
model.setBigImage(isBigImage);
view.buildGUI();
if (HCSData) {
if (measurements == null) {
model.setHCSData(false);
model.fireLoadROIServerOrClient(false);
} else
model.fireLoadROIFromServer(measurements);
} else {
model.fireLoadROIServerOrClient(false);
}
break;
case DISCARDED:
throw new IllegalStateException(
"This method can't be invoked in the DISCARDED state.");
default:
if (!view.isVisible()) postEvent(MeasurementToolLoaded.ADD);
view.deIconify();
view.setVisible(true);
}
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#getUI()
*/
public JFrame getUI() { return view; }
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#discard()
*/
public void discard()
{
if (model.getState() != DISCARDED) {
view.setVisible(false);
model.discard();
fireStateChange();
}
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#setDataChanged()
*/
public void setDataChanged()
{
model.notifyDataChanged(true);
firePropertyChange(ROI_CHANGED_PROPERTY, Boolean.valueOf(false),
Boolean.valueOf(true));
fireStateChange();
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#cancel()
*/
public void cancel()
{
model.cancel();
view.setReadyStatus();
fireStateChange();
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#getState()
*/
public int getState() { return model.getState(); }
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#setROI(InputStream)
*/
public void setROI(InputStream input)
{
//if (model.getState() != LOADING_ROI || input == null) return;
if (model.getState() != LOADING_ROI) return;
if (input == null) {
model.setState(MeasurementViewer.READY);
view.refreshToolBar();
view.rebuildManagerTable();
view.updateDrawingArea();
view.setReadyStatus();
fireStateChange();
//Now we are ready to go. We can post an event to add component to
//Viewer
postEvent(MeasurementToolLoaded.ADD);
return;
}
Registry reg = MeasurementAgent.getRegistry();
Logger log = reg.getLogger();
try {
boolean valid = model.setROI(input);
if (!valid) {
reg.getUserNotifier().notifyInfo("ROI", "The ROI are not " +
"compatible with the image.");
try {
input.close();
} catch (Exception io) {
log.warn(this, "Cannot close the stream "+io.getMessage());
}
//reset
fireStateChange();
return;
}
} catch (Exception e) {
if (e instanceof ParsingException) {
log.error(this, "Cannot parse the ROI for "+model.getImageID());
} else {
}
try {
input.close();
} catch (Exception io) {
log.warn(this, "Cannot close the stream "+io.getMessage());
}
return;
}
view.refreshToolBar();
view.rebuildManagerTable();
view.updateDrawingArea();
view.setReadyStatus();
fireStateChange();
//Now we are ready to go. We can post an event to add component to
//Viewer
postEvent(MeasurementToolLoaded.ADD);
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#setMagnifiedPlane(int, int, double)
*/
public void setMagnifiedPlane(int defaultZ, int defaultT,
double magnification)
{
int z = model.getDefaultZ();
int t = model.getDefaultT();
double f = model.getMagnification();
if (z == defaultZ && t == defaultT) {
if (f != magnification) {
model.setMagnification(magnification);
view.onMagnificationChanged();
}
if (!model.isBigImage()) return;
}
model.setPlane(defaultZ, defaultT);
Drawing drawing = model.getDrawing();
drawing.removeDrawingListener(controller);
drawing.clear();
ShapeList list = null;
try {
list = model.getShapeList();
} catch (Exception e) {
view.handleROIException(e, MeasurementViewerUI.RETRIEVE_MSG);
}
view.setStatus(MeasurementViewerUI.DEFAULT_MSG);
if (list != null) {
TreeMap map = list.getList();
Iterator i = map.values().iterator();
ROIShape shape;
ROIFigure fig;
while (i.hasNext()) {
shape = (ROIShape) i.next();
if (shape != null)
{
fig = shape.getFigure();
drawing.add(fig);
if (fig.canAnnotate())
fig.addFigureListener(controller);
}
}
}
//Reset the result.
view.displayAnalysisResults();
model.getDrawingView().setDrawing(drawing);
drawing.addDrawingListener(controller);
if (f != magnification)
model.setMagnification(magnification);
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#close()
*/
public void close()
{
if (model.getState() == DISCARDED) {
return;
}
/*
if (!model.isDataSaved()) {
String title = "Discard Changes";
String message = "Do you want to exit and discard changes?";
MessageBox dialog = new MessageBox(view, title, message);
if (dialog.showMsgBox() == MessageBox.NO_OPTION) return;
}
model.setDataDiscarded();
*/
//Post event indicating that we don't care about saving.
postEvent(MeasurementToolLoaded.REMOVE);
if (model.isHCSData()) {
List<FileAnnotationData> list = model.getMeasurements();
if (list == null || list.size() == 0) view.setVisible(false);
else discard();
} else view.setVisible(false);
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#iconified(boolean)
*/
public void iconified(boolean b)
{
if (model.getState() == DISCARDED)
throw new IllegalStateException("This method shouldn't be " +
"invoked in the DISCARDED state:"+model.getState());
view.setVisible(b);
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#loadROI()
*/
public void loadROI()
{
FileChooser chooser = createChooserDialog(FileChooser.LOAD);
chooser.setCheckOverride(false);
if (chooser.showDialog() != JFileChooser.APPROVE_OPTION) return;
File f = chooser.getSelectedFile();
if (f == null) return;
model.fireROILoading(f.getAbsolutePath());
fireStateChange();
//view.updateDrawingArea();
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#saveROI()
*/
public void saveROI()
{
FileChooser chooser = createChooserDialog(FileChooser.SAVE);
if (chooser.showDialog() != JFileChooser.APPROVE_OPTION) return;
File file = chooser.getSelectedFile();
if (file == null) return;
String s = file.getAbsolutePath();
if (s == null || s.trim().length() == 0) return;
if (!s.endsWith(XMLFilter.XML)) {
String fileName = s+"."+XMLFilter.XML;
file = new File(fileName);
}
saveBackROI(file.getAbsolutePath());
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#saveROIToServer(boolean)
*/
public void saveROIToServer(boolean close)
{
if (!canAnnotate()) return;
List<ROI> l = model.getROIToDelete();
if (l != null && l.size() > 0) {
List<DeletableObject> objects = new ArrayList<DeletableObject>();
Iterator<ROI> i = l.iterator();
ROI roi;
ROIData data;
SecurityContext ctx = model.getSecurityContext();
DeletableObject d;
while (i.hasNext()) {
roi = i.next();
if (!roi.isClientSide() && roi.canDelete()) {
data = new ROIData();
data.setId(roi.getID());
data.setImage(model.getImage().asImage());
d = new DeletableObject(data);
d.setSecurityContext(ctx);
objects.add(d);
}
}
if (objects.size() == 0) {
model.saveROIToServer(true, close);
} else {
model.deleteAllROIs(objects);
}
} else {
model.saveROIToServer(true, close);
}
fireStateChange();
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#figureAttributeChanged(AttributeKey, ROIFigure)
*/
public void figureAttributeChanged(AttributeKey key, ROIFigure figure)
{
model.figureAttributeChanged(key, figure);
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#showROIAssistant()
*/
public void showROIAssistant()
{
view.showROIAssistant();
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#showROIAssistant(ROI)
*/
public void showROIAssistant(ROI roi)
{
view.showROIAssistant(roi);
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#showMeasurementsInMicrons(boolean)
*/
public void showMeasurementsInMicrons(boolean inMicrons)
{
model.showMeasurementsInMicrons(inMicrons);
view.updateDrawingArea();
view.refreshResultsTable();
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#setActiveChannels(Map)
*/
public void setActiveChannels(Map activeChannels)
{
int state = model.getState();
switch (model.getState()) {
case DISCARDED:
case LOADING_DATA:
throw new IllegalStateException("This method cannot be " +
"invoked in the DISCARDED, LOADING_DATA " +
"state: "+state);
}
model.setActiveChannels(activeChannels);
//Show or hide some shapes if they are visible on a channel or not
TreeMap<Long, ROI> rois = model.getROI();
Collection<ROIFigure> figures = model.getAllFigures();
ROIFigure figure, f;
ROI roi;
TreeMap<Coord3D, ROIShape> shapeMap;
ROIShape shape;
Entry entry;
if (rois != null) {
Iterator j = rois.entrySet().iterator();
Iterator k;
Coord3D coord;
int c;
while (j.hasNext()) {
entry = (Entry) j.next();
roi = (ROI) entry.getValue();
shapeMap = roi.getShapes();
k = shapeMap.entrySet().iterator();
while (k.hasNext()) {
entry = (Entry) k.next();
shape = (ROIShape) entry.getValue();
coord = shape.getCoord3D();
f = shape.getFigure();
c = coord.getChannel();
if (c >= 0) {
if (f.canAnnotate()) {
f.removeFigureListener(controller);
f.setVisible(model.isChannelActive(c));
f.addFigureListener(controller);
}
}
}
}
view.repaint();
}
if (!view.inDataView() || !view.isVisible()) return;
figures = getSelectedFigures();
if (figures.size() != 1) return;
figure = figures.iterator().next();
List<ROIShape> shapeList = new ArrayList<ROIShape>();
roi = figure.getROI();
shapeMap = roi.getShapes();
Iterator j = shapeMap.entrySet().iterator();
while (j.hasNext()) {
entry = (Entry) j.next();
shapeList.add( (ROIShape) entry.getValue());
}
if (shapeList.size() != 0) analyseShapeList(shapeList);
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#setActiveChannelsColor(Map)
*/
public void setActiveChannelsColor(Map channels)
{
int state = model.getState();
switch (model.getState()) {
case DISCARDED:
case LOADING_DATA:
throw new IllegalStateException("This method cannot be " +
"invoked in the DISCARDED, LOADING_DATA " +
"state: "+state);
}
model.setActiveChannels(channels);
if (!view.inDataView() || !view.isVisible()) return;
Collection<ROIFigure> collection = getSelectedFigures();
if (collection.size() != 1) return;
ROIFigure figure = collection.iterator().next();
ArrayList<ROIShape> shapeList = new ArrayList<ROIShape>();
ROI roi = figure.getROI();
TreeMap<Coord3D, ROIShape> shapeMap = roi.getShapes();
Iterator<Coord3D> shapeIterator = shapeMap.keySet().iterator();
while(shapeIterator.hasNext())
shapeList.add(shapeMap.get(shapeIterator.next()));
if (shapeList.size()!=0) analyseShapeList(shapeList);
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#setStatsShapes(Map)
*/
public void setStatsShapes(Map result)
{
int state = model.getState();
if (state != ANALYSE_SHAPE) {
MeasurementAgent.getRegistry().getLogger().debug(this,
"This method can only be invoked " +
"in the ANALYSE_SHAPE state: "+state);
return;
}
model.setAnalysisResults(result);
view.displayAnalysisResults();
fireStateChange();
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#analyseShapeList(List)
*/
public void analyseShapeList(List<ROIShape> shapeList)
{
if (shapeList == null)
throw new IllegalArgumentException("No shape specified.");
int state = model.getState();
switch (model.getState()) {
case DISCARDED:
case LOADING_DATA:
case LOADING_ROI:
throw new IllegalStateException("This method cannot be " +
"invoked in the DISCARDED, LOADING_DATA or " +
"LOADING_ROI state: "+state);
case ANALYSE_SHAPE:
return;
}
if (!validShapeList(shapeList))
return;
if (model.getActiveChannels().size() == 0) {
model.setAnalysisResults(null);
view.displayAnalysisResults();
} else {
model.fireAnalyzeShape(shapeList);
fireStateChange();
}
}
/**
* Check to see if the selected figure contains textFigure
* @param shapeList see above.
* @return see above.
*/
private boolean validShapeList(List<ROIShape> shapeList)
{
for(ROIShape shape : shapeList)
if(shape.getFigure() instanceof MeasureTextFigure)
return false;
return true;
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#getSelectedFigures()
*/
public Collection getSelectedFigures()
{
return model.getSelectedFigures();
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#createSingleFigure(boolean)
*/
public void createSingleFigure(boolean createSingleFig)
{
view.createSingleFigure(createSingleFig);
}
/**
* Saves the ROI without displaying a file chooser.
*
* @param path The absolute path to the file.
*/
private void saveBackROI(String path)
{
Registry reg = MeasurementAgent.getRegistry();
UserNotifier un = reg.getUserNotifier();
try {
model.saveROI(path, false);
} catch (ParsingException e) {
reg.getLogger().error(this, "Cannot save the ROI "+e.getMessage());
un.notifyInfo("Save ROI", "Cannot save ROI " +
"for "+model.getImageID());
}
un.notifyInfo("Save ROI", "The Regions of Interests have been " +
"successfully saved. ");
firePropertyChange(ROI_CHANGED_PROPERTY, Boolean.valueOf(false),
Boolean.valueOf(true));
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#toFront()
*/
public void toFront()
{
if (model.getState() == DISCARDED) return;
controller.toFront();
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#setIconImage(BufferedImage)
*/
public void setIconImage(BufferedImage thumbnail)
{
if (model.getState() == DISCARDED) return;
//view.setIconImage(thumbnail);
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#setRndImage(Object)
*/
public void setRndImage(Object rndImage)
{
if (model.getState() == DISCARDED) return;
model.setRenderedImage(rndImage);
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#hasROIToSave()
*/
public boolean hasROIToSave()
{
if (model.getState() == DISCARDED) return false;
return model.hasROIToSave();
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#setServerROI(Collection)
*/
public void setServerROI(Collection result)
{
if (model.getState() != LOADING_ROI)
throw new IllegalArgumentException("The method can only " +
"be invoked in the LOADING_ROI state.");
List<DataObject> nodes = null;
try {
if (result != null) { //some ROI previously saved.
nodes = model.setServerROI(result);
}
} catch (Exception e) {
String s = "Cannot convert server ROI into UI objects:";
MeasurementAgent.getRegistry().getLogger().error(this, s+e);
}
//bring up the UI.
view.layoutUI();
view.updateDrawingArea();
fireStateChange();
//Now we are ready to go. We can post an event to add component to
//Viewer
postEvent(MeasurementToolLoaded.ADD);
if (CollectionUtils.isNotEmpty(nodes)) {
model.fireLoadROIAnnotations(nodes);
}
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#isHCSData()
*/
public boolean isHCSData() { return model.isHCSData(); }
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#getViewTitle()
*/
public String getViewTitle() { return model.getImageTitle(); }
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#setLoadingFromServerClient(Collection)
*/
public void setLoadingFromServerClient(Collection result)
{
if (model.getState() != LOADING_ROI)
throw new IllegalArgumentException("The method can only " +
"be invoked in the LOADING_ROI state.");
List<DataObject> nodes = null;
try
{
boolean hasResult = false;
if (result != null) {
Iterator<ROIResult> i = result.iterator();
ROIResult roiResult;
if (i.hasNext())
{
roiResult = i.next();
if (CollectionUtils.isNotEmpty(roiResult.getROIs()))
hasResult = true;
}
}
if (hasResult) {
//some ROI previously saved.
//result.ge
nodes = model.setServerROI(result);
} else {
model.fireROILoading(null);
return;
}
} catch (Exception e) {
String s = "Cannot convert server ROI into UI objects:";
MeasurementAgent.getRegistry().getLogger().error(this, s+e);
UserNotifier un = MeasurementAgent.getRegistry().getUserNotifier();
un.notifyInfo("Load ROI", "Cannot display the ROI.");
}
view.refreshToolBar();
view.rebuildManagerTable();
view.refreshResultsTable();
view.updateDrawingArea();
view.setReadyStatus();
fireStateChange();
//Now we are ready to go. We can post an event to add component to
//Viewer
postEvent(MeasurementToolLoaded.ADD);
if (CollectionUtils.isNotEmpty(nodes)) {
model.fireLoadROIAnnotations(nodes);
}
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#setUpdateROIComponent(Collection)
*/
public void setUpdateROIComponent(Collection result)
{
Registry reg = MeasurementAgent.getRegistry();
UserNotifier un = reg.getUserNotifier();
try {
model.removeAllROI();
view.rebuildManagerTable();
view.clearInspector();
view.refreshResultsTable();
view.updateDrawingArea();
} catch (NoSuchROIException e) {
reg.getLogger().error(this, "Cannot save the ROI "+e.getMessage());
un.notifyInfo("Save ROI", "Cannot save ROI " +
"for "+model.getImageID());
}
model.fireLoadROIServerOrClient(false);
}
@Override
public long getImageID() {
return model.getImageID();
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#canAnnotate()
*/
public boolean canAnnotate()
{
if (model.getState() == DISCARDED) return false;
//Check if current user can write in object
ExperimenterData exp =
(ExperimenterData) MeasurementAgent.getUserDetails();
long id = exp.getId();
Object ref = model.getRefObject();
boolean b = EditorUtil.isUserOwner(ref, id);
if (b) return b;
if (ref instanceof DataObject) {
return ((DataObject) ref).canAnnotate();
}
return false;
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#canDelete()
*/
public boolean canDelete()
{
if (model.getState() == DISCARDED) return false;
//Check if current user can write in object
ExperimenterData exp =
(ExperimenterData) MeasurementAgent.getUserDetails();
long id = exp.getId();
Object ref = model.getRefObject();
boolean b = EditorUtil.isUserOwner(ref, id);
if (b) return b;
if (ref instanceof DataObject) {
return ((DataObject) ref).canDelete();
}
return false;
}
/**
* Overridden to return the name of the instance to save.
* @see #toString()
*/
public String toString()
{
return "ROI for: "+EditorUtil.truncate(model.getImageName());
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#deleteAllROIs()
*/
public void deleteAllROIs(int level)
{
if (!canDelete()) return;
List<ROIData> list;
if (model.isMember()) level = MeasurementViewer.ME;
list = model.getROIData(level);
if (list.size() == 0) return;
List<DeletableObject> l = new ArrayList<DeletableObject>();
Iterator<ROIData> i = list.iterator();
ROIData roi;
SecurityContext ctx = model.getSecurityContext();
DeletableObject d;
while (i.hasNext()) {
roi = i.next();
if (roi.getId() > 0) {
d = new DeletableObject(roi);
d.setSecurityContext(ctx);
l.add(d);
}
}
//if (l.size() == 0) return;
//clear view. and table.
ExperimenterData exp =
(ExperimenterData) MeasurementAgent.getUserDetails();
try {
List<ROIFigure> figures = model.removeAllROI(exp.getId(), level);
if (figures != null) {
//clear all tables.
view.deleteROIs(figures);
model.getROIComponent().reset();
}
} catch (Exception e) {
LogMessage msg = new LogMessage();
msg.print("Delete ROI");
msg.print(e);
MeasurementAgent.getRegistry().getLogger().error(this, msg);
}
model.deleteAllROIs(l);
fireStateChange();
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#hasROIToDelete()
*/
public boolean hasROIToDelete()
{
if (model.getState() == DISCARDED) return false;
return model.hasROIToDelete();
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#isMember()
*/
public boolean isMember() { return model.isMember(); }
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#isMember()
*/
public void onUpdatedChannels(List<ChannelData> channels)
{
if (model.getState() == DISCARDED) return;
model.setChannelData(channels);
view.displayAnalysisResults();
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#exportGraph()
*/
public void exportGraph() {
view.exportGraph();
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#setROIAnnotations(Map)
*/
public void setROIAnnotations(Map<DataObject, StructuredDataResults> result)
{
if (model.getState() == DISCARDED ||
result == null || result.size() == 0) return;
//Update the UI elements
Collection<ROIFigure> figures = model.getAllFigures();
if (CollectionUtils.isEmpty(figures)) return;
Iterator<ROIFigure> i = figures.iterator();
ROIFigure f;
ShapeData shape;
Map<Long, StructuredDataResults> r = convertMap(result, null);
if (r == null) return;
while (i.hasNext()) {
f = i.next();
shape = f.getROIShape().getData();
if (shape != null) {
f.setAttribute(AnnotationKeys.TAG, r.get(shape.getId()));
}
}
Collection<Figure> figs = view.getSelectedFiguresFromTables();
if (CollectionUtils.isNotEmpty(figs)) {
List<ROIShape> shapes = new ArrayList<ROIShape>();
Iterator<Figure> j = figs.iterator();
while (j.hasNext()) {
f = (ROIFigure) j.next();
shapes.add(f.getROIShape());
}
view.displayAnnotations(shapes);
}
}
/**
* Converts the results map.
*
* @param result The map to handle
* @param type The type of object to look for or <code>null</code>.
* @return See above.
*/
private Map<Long, StructuredDataResults> convertMap(
Map<DataObject, StructuredDataResults> result, Class<?> type) {
Map<Long, StructuredDataResults> r = new HashMap<Long, StructuredDataResults>();
Entry<DataObject, StructuredDataResults> e;
Iterator<Entry<DataObject, StructuredDataResults>>
i = result.entrySet().iterator();
while (i.hasNext()) {
e = i.next();
if (type == null) {
r.put(e.getKey().getId(), e.getValue());
} else {
if (e.getKey().getClass().equals(type)) {
r.put(e.getKey().getId(), e.getValue());
}
}
}
return r;
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#setExistingTags(Collection)
*/
public void setExistingTags(Collection tags) {
model.setExistingTags(tags);
//Display the UI.
Collection<Figure> shapes = model.getSelectedFigures();
Iterator<Figure> i = shapes.iterator();
ROIFigure shape;
StructuredDataResults data;
List<Object> l = new ArrayList<Object>();
List<Long> ids = new ArrayList<Long>();
boolean valid = tags != null && CollectionUtils.isNotEmpty(tags);
TagAnnotationData d;
while (i.hasNext()) {
shape = (ROIFigure) i.next();
data = (StructuredDataResults) shape.getAttribute(AnnotationKeys.TAG);
if (data != null && CollectionUtils.isNotEmpty(data.getTags())) {
if (valid) {
Iterator<TagAnnotationData> j = data.getTags().iterator();
while (j.hasNext()) {
d = j.next();
ids.add(d.getId());
l.add(d);
}
} else {
l.addAll(data.getTags());
}
}
}
//if tags not empty.
List<Object> available = new ArrayList<Object>();
if (valid) {
Iterator j = tags.iterator();
while (j.hasNext()) {
AnnotationData object = (AnnotationData) j.next();
if (!ids.contains(object.getId())) {
available.add(object);
}
}
}
//Bring up the selection Wizard
SelectionWizard wizard = new SelectionWizard(
view, available, l, TagAnnotationData.class, true,
model.getCurrentUser());
wizard.addPropertyChangeListener(controller);
UIUtilities.centerAndShow(wizard);
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#loadTags()
*/
public void loadTags() {
Collection tags = model.getExistingTags();
if (tags == null) {
model.fireExistingTagsLoading();
} else {
setExistingTags(tags);
}
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#tagSelectedFigures()
*/
public void tagSelectedFigures(List<AnnotationData> tags)
{
Collection<Figure> figures = view.getSelectedFiguresFromTables();
if (CollectionUtils.isEmpty(figures)) {
return;
}
List<ROIShape> shapes = new ArrayList<ROIShape>();
Iterator<Figure> kk = figures.iterator();
ROIFigure fig;
while (kk.hasNext())
{
fig = (ROIFigure) kk.next();
shapes.add(fig.getROIShape());
}
if (CollectionUtils.isEmpty(shapes)) return;
Multimap<Long, AnnotationData> m = ArrayListMultimap.create();
Iterator<AnnotationData> j = tags.iterator();
AnnotationData an;
while (j.hasNext()) {
an = j.next();
m.put(an.getId(), an);
}
Iterator<ROIShape> i = shapes.iterator();
ROIShape shape;
StructuredDataResults data;
List<DataObject> objects = new ArrayList<DataObject>();
ShapeData d;
Map<Long, AnnotationData> mo = new HashMap<Long, AnnotationData>();
while (i.hasNext()) {
shape = i.next();
d = shape.getData();
if (d != null && d.getId() > 0) {
objects.add(d);
data = (StructuredDataResults)
shape.getFigure().getAttribute(AnnotationKeys.TAG);
if (data != null && CollectionUtils.isNotEmpty(data.getTags())) {
Collection<TagAnnotationData> t = data.getTags();
Iterator<TagAnnotationData> tt = t.iterator();
while (tt.hasNext()) {
TagAnnotationData tag = tt.next();
if (!mo.containsKey(tag.getId())) {
mo.put(tag.getId(), tag);
}
}
}
}
}
if (objects.isEmpty()) {
UserNotifier un = MeasurementAgent.getRegistry().getUserNotifier();
un.notifyInfo("ROI Annotations",
"You must save the ROI before annotating it.");
return;
}
//Now we prepare the list of annotations to add or remove
List<AnnotationData> toAdd = new ArrayList<AnnotationData>();
List<Object> toRemove = new ArrayList<Object>();
if (CollectionUtils.isNotEmpty(m.get(-1L))) {
toAdd.addAll(m.removeAll(-1L));
}
Iterator<Entry<Long, AnnotationData>> k = m.entries().iterator();
Entry<Long, AnnotationData> e;
while (k.hasNext()) {
e = k.next();
Long id = e.getKey();
if (!mo.containsKey(id)) {
toAdd.add(e.getValue());
}
}
k = mo.entrySet().iterator();
while (k.hasNext()) {
e = k.next();
Long id = e.getKey();
if (!m.containsKey(id)) {
toRemove.add(e.getValue());
}
}
model.fireAnnotationSaving(objects, toAdd, toRemove);
}
/**
* Implemented as specified by the {@link MeasurementViewer} interface.
* @see MeasurementViewer#onAnnotationSaved()
*/
public void onAnnotationSaved() {
//Load the annotation for the selected shapes.
//Display the UI.
Collection<ROIFigure> figures = model.getAllFigures();
if (CollectionUtils.isEmpty(figures)) {
return;
}
List<ROIShape> shapes = new ArrayList<ROIShape>();
Iterator<ROIFigure> kk = figures.iterator();
ROIFigure fig;
while (kk.hasNext())
{
fig = kk.next();
shapes.add(fig.getROIShape());
}
if (CollectionUtils.isEmpty(shapes)) return;
Iterator<ROIShape> i = shapes.iterator();
ROIShape shape;
List<DataObject> nodes = new ArrayList<DataObject>();
while (i.hasNext()) {
shape = (ROIShape) i.next();
nodes.add(shape.getData());
}
model.fireLoadROIAnnotations(nodes);
}
}