/**
*
*/
package icy.roi;
import icy.plugin.PluginDescriptor;
import icy.plugin.PluginLauncher;
import icy.plugin.PluginLoader;
import icy.plugin.interface_.PluginROIDescriptor;
import icy.roi.ROIEvent.ROIEventType;
import icy.sequence.Sequence;
import icy.sequence.SequenceEvent;
import icy.system.IcyExceptionHandler;
import icy.util.StringUtil;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import plugins.kernel.roi.descriptor.measure.ROIBasicMeasureDescriptorsPlugin;
/**
* Abstract class providing the basic methods to retrieve properties and compute a specific
* descriptor for a region of interest (ROI)
*
* @author Stephane Dallongeville, Alexandre Dufour
*/
public abstract class ROIDescriptor
{
/**
* Returns all available ROI descriptors (see {@link ROIDescriptor}) and their attached plugin
* (see {@link PluginROIDescriptor}).<br/>
* This list can be extended by installing new plugin(s) implementing the {@link PluginROIDescriptor} interface.
*
* @see ROIDescriptor#compute(ROI, Sequence)
* @see PluginROIDescriptor#compute(ROI, Sequence)
*/
public static Map<ROIDescriptor, PluginROIDescriptor> getDescriptors()
{
final Map<ROIDescriptor, PluginROIDescriptor> result = new HashMap<ROIDescriptor, PluginROIDescriptor>();
final List<PluginDescriptor> pluginDescriptors = PluginLoader.getPlugins(PluginROIDescriptor.class);
for (PluginDescriptor pluginDescriptor : pluginDescriptors)
{
try
{
final PluginROIDescriptor plugin = (PluginROIDescriptor) PluginLauncher.create(pluginDescriptor);
final List<ROIDescriptor> descriptors = plugin.getDescriptors();
if (descriptors != null)
{
for (ROIDescriptor roiDescriptor : descriptors)
result.put(roiDescriptor, plugin);
}
}
catch (Throwable e)
{
// show a message in the output console
IcyExceptionHandler.showErrorMessage(e, false, true);
// and send an error report (silent as we don't want a dialog appearing here)
IcyExceptionHandler.report(pluginDescriptor, IcyExceptionHandler.getErrorMessage(e, true));
}
}
return result;
}
/**
* Returns the descriptor identified by the given id from the given list of {@link ROIDescriptor}.<br>
* It can return <code>null</code> if the descriptor is not found in the given list.
*
* @param id
* the id of the descriptor ({@link ROIBasicMeasureDescriptorsPlugin#ID_VOLUME} for instance) @see
* #getDescriptors()
* @see #computeDescriptor(String, ROI, Sequence)
*/
public static ROIDescriptor getDescriptor(Collection<ROIDescriptor> descriptors, String id)
{
for (ROIDescriptor roiDescriptor : descriptors)
if (StringUtil.equals(roiDescriptor.getId(), id))
return roiDescriptor;
return null;
}
/**
* Computes the specified descriptor from the input {@link ROIDescriptor} set on given ROI
* and returns the result (or <code>null</code> if the descriptor is not found).
*
* @param roiDescriptors
* the input {@link ROIDescriptor} set (see {@link #getDescriptors()} method)
* @param descriptorId
* the id of the descriptor we want to compute ({@link ROIBasicMeasureDescriptorsPlugin#ID_VOLUME} for
* instance)
* @param roi
* the ROI on which the descriptor(s) should be computed
* @param sequence
* an optional sequence where the pixel size can be retrieved
* @return the computed descriptor or <code>null</code> if the descriptor if not found in the
* specified set
* @throws UnsupportedOperationException
* if the type of the given ROI is not supported by this descriptor, or if <code>sequence</code> is
* <code>null</code> while the calculation requires it, or if
* the specified Z, T or C position are not supported by the descriptor
*/
public static Object computeDescriptor(Collection<ROIDescriptor> roiDescriptors, String descriptorId, ROI roi,
Sequence sequence)
{
final ROIDescriptor roiDescriptor = getDescriptor(roiDescriptors, descriptorId);
if (roiDescriptor != null)
return roiDescriptor.compute(roi, sequence);
return null;
}
/**
* Computes the specified descriptor on given ROI and returns the result (or <code>null</code> if the descriptor is
* not found).
*
* @param descriptorId
* the id of the descriptor we want to compute ({@link ROIBasicMeasureDescriptorsPlugin#ID_VOLUME} for
* instance)
* @param roi
* the ROI on which the descriptor(s) should be computed
* @param sequence
* an optional sequence where the pixel size can be retrieved
* @return the computed descriptor or <code>null</code> if the descriptor if not found in the
* specified set
* @throws UnsupportedOperationException
* if the type of the given ROI is not supported by this descriptor, or if <code>sequence</code> is
* <code>null</code> while the calculation requires it, or if
* the specified Z, T or C position are not supported by the descriptor
*/
public static Object computeDescriptor(String descriptorId, ROI roi, Sequence sequence)
{
return computeDescriptor(getDescriptors().keySet(), descriptorId, roi, sequence);
}
protected final String id;
protected final String name;
protected final Class<?> type;
/**
* Create a new {@link ROIDescriptor} with given id, name and type
*/
protected ROIDescriptor(String id, String name, Class<?> type)
{
super();
this.id = id;
this.name = name;
this.type = type;
}
/**
* Create a new {@link ROIDescriptor} with given name and type
*/
protected ROIDescriptor(String name, Class<?> type)
{
this(name, name, type);
}
/**
* Returns the id of this descriptor.<br/>
* By default it uses the descriptor's name but it can be overridden to be different.
*/
public String getId()
{
return id;
}
/**
* Returns the name of this descriptor.<br/>
* The name is used as title (column header) in the ROI panel so keep it short and self
* explanatory.
*/
public String getName()
{
return name;
};
/**
* Returns a single line description (used as tooltip) for this descriptor
*/
public abstract String getDescription();
/**
* Returns the unit of this descriptor (<code>ex: "px", "mm", "�m2"...</code>).</br>
* It can return an empty or <code>null</code> string (default implementation) if there is no
* specific unit attached to the descriptor.<br/>
* Note that unit is concatenated to the name to build the title (column header) in the ROI
* panel.
*
* @param sequence
* the sequence on which we want to compute the descriptor (if required) to get access to
* the pixel size informations and return according unit
*/
public String getUnit(Sequence sequence)
{
return null;
}
/**
* Returns the type of result for this descriptor
*
* @see #compute(ROI, Sequence)
*/
public Class<?> getType()
{
return type;
};
/**
* Returns <code>true</code> if this descriptor compute its result on {@link Sequence} data and *per channel* (as
* pixel intensity information).<br>
* By default it returns <code>false</code>, override this method if a descriptor require per channel computation.
*
* @see #compute(ROI, Sequence)
*/
public boolean separateChannel()
{
return false;
}
/**
* Returns <code>true</code> if this descriptor need to be recomputed when the specified Sequence change event
* happen.<br>
* By default it returns <code>false</code>, override this method if a descriptor need a specific implementation.
*
* @see #compute(ROI, Sequence)
*/
public boolean needRecompute(SequenceEvent change)
{
return false;
}
/**
* Returns <code>true</code> if this descriptor need to be recomputed when the specified ROI change event happen.<br>
* By default it returns <code>true</code> on ROI content change, override this method if a descriptor need a
* specific implementation.
*
* @see #compute(ROI, Sequence)
*/
public boolean needRecompute(ROIEvent change)
{
return (change.getType() == ROIEventType.ROI_CHANGED);
};
/**
* Computes the descriptor on the specified ROI and return the result.
*
* @param roi
* the ROI on which the descriptor(s) should be computed
* @param sequence
* an optional sequence where the pixel informations can be retrieved (see {@link #separateChannel()})
* @return the result of this descriptor computed from the specified parameters.
* @throws UnsupportedOperationException
* if the type of the given ROI is not supported by this descriptor, or if <code>sequence</code> is
* <code>null</code> while the calculation requires it
*/
public abstract Object compute(ROI roi, Sequence sequence) throws UnsupportedOperationException;
/*
* We want a unique id for each {@link ROIDescriptor}
*/
@Override
public boolean equals(Object obj)
{
if (obj instanceof ROIDescriptor)
return StringUtil.equals(((ROIDescriptor) obj).getId(), getId());
return super.equals(obj);
}
/*
* We want a unique id for each {@link ROIDescriptor}
*/
@Override
public int hashCode()
{
return getId().hashCode();
}
@Override
public String toString()
{
// default implementation
return getName();
}
}