001/** 002 * 003 */ 004package icy.roi; 005 006import icy.plugin.PluginDescriptor; 007import icy.plugin.PluginLauncher; 008import icy.plugin.PluginLoader; 009import icy.plugin.interface_.PluginROIDescriptor; 010import icy.roi.ROIEvent.ROIEventType; 011import icy.sequence.Sequence; 012import icy.sequence.SequenceEvent; 013import icy.system.IcyExceptionHandler; 014import icy.util.StringUtil; 015 016import java.util.Collection; 017import java.util.HashMap; 018import java.util.List; 019import java.util.Map; 020 021import plugins.kernel.roi.descriptor.measure.ROIBasicMeasureDescriptorsPlugin; 022 023/** 024 * Abstract class providing the basic methods to retrieve properties and compute a specific 025 * descriptor for a region of interest (ROI) 026 * 027 * @author Stephane Dallongeville, Alexandre Dufour 028 */ 029public abstract class ROIDescriptor 030{ 031 /** 032 * Returns all available ROI descriptors (see {@link ROIDescriptor}) and their attached plugin 033 * (see {@link PluginROIDescriptor}).<br/> 034 * This list can be extended by installing new plugin(s) implementing the {@link PluginROIDescriptor} interface. 035 * 036 * @see ROIDescriptor#compute(ROI, Sequence) 037 * @see PluginROIDescriptor#compute(ROI, Sequence) 038 */ 039 public static Map<ROIDescriptor, PluginROIDescriptor> getDescriptors() 040 { 041 final Map<ROIDescriptor, PluginROIDescriptor> result = new HashMap<ROIDescriptor, PluginROIDescriptor>(); 042 final List<PluginDescriptor> pluginDescriptors = PluginLoader.getPlugins(PluginROIDescriptor.class); 043 044 for (PluginDescriptor pluginDescriptor : pluginDescriptors) 045 { 046 try 047 { 048 final PluginROIDescriptor plugin = (PluginROIDescriptor) PluginLauncher.create(pluginDescriptor); 049 final List<ROIDescriptor> descriptors = plugin.getDescriptors(); 050 051 if (descriptors != null) 052 { 053 for (ROIDescriptor roiDescriptor : descriptors) 054 result.put(roiDescriptor, plugin); 055 } 056 } 057 catch (Throwable e) 058 { 059 // show a message in the output console 060 IcyExceptionHandler.showErrorMessage(e, false, true); 061 // and send an error report (silent as we don't want a dialog appearing here) 062 IcyExceptionHandler.report(pluginDescriptor, IcyExceptionHandler.getErrorMessage(e, true)); 063 } 064 } 065 066 return result; 067 } 068 069 /** 070 * Returns the descriptor identified by the given id from the given list of {@link ROIDescriptor}.<br> 071 * It can return <code>null</code> if the descriptor is not found in the given list. 072 * 073 * @param id 074 * the id of the descriptor ({@link ROIBasicMeasureDescriptorsPlugin#ID_VOLUME} for instance) @see 075 * #getDescriptors() 076 * @see #computeDescriptor(String, ROI, Sequence) 077 */ 078 public static ROIDescriptor getDescriptor(Collection<ROIDescriptor> descriptors, String id) 079 { 080 for (ROIDescriptor roiDescriptor : descriptors) 081 if (StringUtil.equals(roiDescriptor.getId(), id)) 082 return roiDescriptor; 083 084 return null; 085 } 086 087 /** 088 * Returns the descriptor identified by the given id from the given list of {@link ROIDescriptor}.<br> 089 * It can return <code>null</code> if the descriptor is not found in the given list. 090 * 091 * @param id 092 * the id of the descriptor ({@link ROIBasicMeasureDescriptorsPlugin#ID_VOLUME} for instance) @see 093 * #getDescriptors() 094 * @see #computeDescriptor(String, ROI, Sequence) 095 */ 096 public static ROIDescriptor getDescriptor(String id) 097 { 098 return getDescriptor(getDescriptors().keySet(), id); 099 } 100 101 /** 102 * Computes the specified descriptor from the input {@link ROIDescriptor} set on given ROI 103 * and returns the result (or <code>null</code> if the descriptor is not found). 104 * 105 * @param roiDescriptors 106 * the input {@link ROIDescriptor} set (see {@link #getDescriptors()} method) 107 * @param descriptorId 108 * the id of the descriptor we want to compute ({@link ROIBasicMeasureDescriptorsPlugin#ID_VOLUME} for 109 * instance) 110 * @param roi 111 * the ROI on which the descriptor(s) should be computed 112 * @param sequence 113 * an optional sequence where the pixel size can be retrieved 114 * @return the computed descriptor or <code>null</code> if the descriptor if not found in the 115 * specified set 116 * @throws UnsupportedOperationException 117 * if the type of the given ROI is not supported by this descriptor, or if <code>sequence</code> is 118 * <code>null</code> while the calculation requires it, or if 119 * the specified Z, T or C position are not supported by the descriptor 120 */ 121 public static Object computeDescriptor(Collection<ROIDescriptor> roiDescriptors, String descriptorId, ROI roi, 122 Sequence sequence) 123 { 124 final ROIDescriptor roiDescriptor = getDescriptor(roiDescriptors, descriptorId); 125 126 if (roiDescriptor != null) 127 return roiDescriptor.compute(roi, sequence); 128 129 return null; 130 } 131 132 /** 133 * Computes the specified descriptor on given ROI and returns the result (or <code>null</code> if the descriptor is 134 * not found). 135 * 136 * @param descriptorId 137 * the id of the descriptor we want to compute ({@link ROIBasicMeasureDescriptorsPlugin#ID_VOLUME} for 138 * instance) 139 * @param roi 140 * the ROI on which the descriptor(s) should be computed 141 * @param sequence 142 * an optional sequence where the pixel size can be retrieved 143 * @return the computed descriptor or <code>null</code> if the descriptor if not found in the 144 * specified set 145 * @throws UnsupportedOperationException 146 * if the type of the given ROI is not supported by this descriptor, or if <code>sequence</code> is 147 * <code>null</code> while the calculation requires it, or if 148 * the specified Z, T or C position are not supported by the descriptor 149 */ 150 public static Object computeDescriptor(String descriptorId, ROI roi, Sequence sequence) 151 { 152 return computeDescriptor(getDescriptors().keySet(), descriptorId, roi, sequence); 153 } 154 155 protected final String id; 156 protected final String name; 157 protected final Class<?> type; 158 159 /** 160 * Create a new {@link ROIDescriptor} with given id, name and type 161 */ 162 protected ROIDescriptor(String id, String name, Class<?> type) 163 { 164 super(); 165 166 this.id = id; 167 this.name = name; 168 this.type = type; 169 } 170 171 /** 172 * Create a new {@link ROIDescriptor} with given name and type 173 */ 174 protected ROIDescriptor(String name, Class<?> type) 175 { 176 this(name, name, type); 177 } 178 179 /** 180 * Returns the id of this descriptor.<br/> 181 * By default it uses the descriptor's name but it can be overridden to be different. 182 */ 183 public String getId() 184 { 185 return id; 186 } 187 188 /** 189 * Returns the name of this descriptor.<br/> 190 * The name is used as title (column header) in the ROI panel so keep it short and self 191 * explanatory. 192 */ 193 public String getName() 194 { 195 return name; 196 }; 197 198 /** 199 * Returns a single line description (used as tooltip) for this descriptor 200 */ 201 public abstract String getDescription(); 202 203 /** 204 * Returns the unit of this descriptor (<code>ex: "px", "mm", "µm2"...</code>).</br> 205 * It can return an empty or <code>null</code> string (default implementation) if there is no 206 * specific unit attached to the descriptor.<br/> 207 * Note that unit is concatenated to the name to build the title (column header) in the ROI 208 * panel. 209 * 210 * @param sequence 211 * the sequence on which we want to compute the descriptor (if required) to get access to 212 * the pixel size informations and return according unit 213 */ 214 public String getUnit(Sequence sequence) 215 { 216 return null; 217 } 218 219 /** 220 * Returns the type of result for this descriptor 221 * 222 * @see #compute(ROI, Sequence) 223 */ 224 public Class<?> getType() 225 { 226 return type; 227 }; 228 229 /** 230 * Returns <code>true</code> if this descriptor compute its result on {@link Sequence} data and *per channel* (as 231 * pixel intensity information).<br> 232 * By default it returns <code>false</code>, override this method if a descriptor require per channel computation. 233 * 234 * @see #compute(ROI, Sequence) 235 */ 236 public boolean separateChannel() 237 { 238 return false; 239 } 240 241 /** 242 * Returns <code>true</code> if this descriptor need to be recomputed when the specified Sequence change event 243 * happen.<br> 244 * By default it returns <code>false</code>, override this method if a descriptor need a specific implementation. 245 * 246 * @see #compute(ROI, Sequence) 247 */ 248 public boolean needRecompute(SequenceEvent change) 249 { 250 return false; 251 } 252 253 /** 254 * Returns <code>true</code> if this descriptor need to be recomputed when the specified ROI change event happen.<br> 255 * By default it returns <code>true</code> on ROI content change, override this method if a descriptor need a 256 * specific implementation. 257 * 258 * @see #compute(ROI, Sequence) 259 */ 260 public boolean needRecompute(ROIEvent change) 261 { 262 return (change.getType() == ROIEventType.ROI_CHANGED); 263 }; 264 265 /** 266 * Computes the descriptor on the specified ROI and return the result. 267 * 268 * @param roi 269 * the ROI on which the descriptor(s) should be computed 270 * @param sequence 271 * an optional sequence where the pixel informations can be retrieved (see {@link #separateChannel()}) 272 * @return the result of this descriptor computed from the specified parameters. 273 * @throws UnsupportedOperationException 274 * if the type of the given ROI is not supported by this descriptor, or if <code>sequence</code> is 275 * <code>null</code> while the calculation requires it 276 */ 277 public abstract Object compute(ROI roi, Sequence sequence) throws UnsupportedOperationException; 278 279 /* 280 * We want a unique id for each {@link ROIDescriptor} 281 */ 282 @Override 283 public boolean equals(Object obj) 284 { 285 if (obj instanceof ROIDescriptor) 286 return StringUtil.equals(((ROIDescriptor) obj).getId(), getId()); 287 288 return super.equals(obj); 289 } 290 291 /* 292 * We want a unique id for each {@link ROIDescriptor} 293 */ 294 @Override 295 public int hashCode() 296 { 297 return getId().hashCode(); 298 } 299 300 @Override 301 public String toString() 302 { 303 // default implementation 304 return getName(); 305 } 306}