001 package jigcell.compare.impl;
002
003 import java.awt.EventQueue;
004 import java.beans.PropertyChangeEvent;
005 import java.beans.PropertyChangeListener;
006 import java.beans.PropertyChangeSupport;
007 import jigcell.compare.IDataGenerator;
008 import jigcell.compare.IProgrammableDataGenerator;
009
010 /**
011 * Evaluates a list of data generators on a background thread.
012 *
013 * <p>
014 * This code is licensed under the DARPA BioCOMP Open Source License. See LICENSE for more details.
015 * </p>
016 *
017 * @author Nicholas Allen
018 */
019
020 public class DataGeneratorEvaluator implements PropertyChangeListener {
021
022 /**
023 * Option for name to display when evaluating a generator
024 */
025
026 public final static String OPTION_EVALUATIONNAME = "DataGeneratorEvaluator.evaluationDisplayName";
027
028 /**
029 * Option for indicating the target result when evaluating a generator
030 */
031
032 public final static String OPTION_EVALUATIONTARGET = "DataGeneratorEvaluator.evaluationTarget";
033
034 /**
035 * Event name for cancelling the evaluation
036 */
037
038 public final static String PROPERTY_CANCEL = "DataGeneratorEvaluator.quit";
039
040 /**
041 * Event name for a complete evaluation
042 */
043
044 public final static String PROPERTY_EVALUATIONFINISHED = "DataGeneratorEvaluator.evaluateFinished";
045
046 /**
047 * Event name for updates
048 */
049
050 public final static String PROPERTY_EVALUATIONUPDATE = "DataGeneratorEvaluator.evaluateUpdate";
051
052 /**
053 * Event name for a message update
054 */
055
056 public final static String PROPERTY_MESSAGE = "DataGeneratorEvaluator.message";
057
058 /**
059 * Event name for a progress update
060 */
061
062 public final static String PROPERTY_PROGRESS = "DataGeneratorEvaluator.progress";
063
064 /**
065 * The list of generators
066 */
067
068 protected IDataGenerator generators [];
069
070 /**
071 * Whether the evaluation was cancelled
072 */
073
074 protected boolean cancel;
075
076 /**
077 * The data generator currently being evaluated
078 */
079
080 protected IDataGenerator currentGenerator;
081
082 /**
083 * Current position in the list of generators
084 */
085
086 protected int position;
087
088 /**
089 * Callback for processing generators prior to execution
090 */
091
092 protected IProcessor processor;
093
094 /**
095 * Support for handling property change events
096 */
097
098 private PropertyChangeSupport propertySupport;
099
100 /**
101 * Thread for evaluation
102 */
103
104 private Thread executor;
105
106 /**
107 * Performs any needed processing on the data generator before evaluation.
108 */
109
110 public interface IProcessor {
111
112 /**
113 * Creates a replacement data generator for evaluation. This callback will be made on the event thread. No processing will be done on
114 * the generator prior to performing this callback.
115 *
116 * @param generator Generator
117 */
118
119 IDataGenerator processGenerator (IDataGenerator generator);
120 }
121
122 /**
123 * Creates a new evaluator for data generators.
124 *
125 * @param generators List of generators to evaluate
126 */
127
128 public DataGeneratorEvaluator (IDataGenerator generators []) {
129 this (generators, null);
130 }
131
132 /**
133 * Creates a new evaluator for data generators.
134 *
135 * @param generators List of generators to evaluate
136 * @param processor Optional component to process a generator before evaluation
137 */
138
139 public DataGeneratorEvaluator (IDataGenerator generators [], IProcessor processor) {
140 if (generators == null)
141 throw new IllegalArgumentException ("List of generators cannot be null.");
142 this.generators = generators;
143 this.processor = processor;
144 propertySupport = new PropertyChangeSupport (this);
145 }
146
147 /**
148 * Adds a PropertyChangeListener to the listener list.
149 *
150 * @param listener Listener
151 */
152
153 public void addPropertyChangeListener (PropertyChangeListener listener) {
154 propertySupport.addPropertyChangeListener (listener);
155 }
156
157 /**
158 * Attempts to cancel the current evaluation.
159 */
160
161 public synchronized void cancel () {
162 if (cancel)
163 return;
164 cancel = true;
165 if (executor != null)
166 executor.interrupt ();
167 }
168
169 /**
170 * Starts evaluating the given generators.
171 */
172
173 public synchronized void execute () {
174 if (executor != null)
175 throw new IllegalThreadStateException ("This evaluator is already running.");
176 cancel = false;
177 executor = new Thread (ProxyBuilder.proxyRunnable (this, "evaluate"));
178 executor.start ();
179 }
180
181 public void propertyChange (PropertyChangeEvent e) {
182 if (PROPERTY_CANCEL.equals (e.getPropertyName ()))
183 cancel ();
184 }
185
186 /**
187 * Removes a PropertyChangeListener from the listener list.
188 *
189 * @param listener Listener
190 */
191
192 public void removePropertyChangeListener (PropertyChangeListener listener) {
193 propertySupport.removePropertyChangeListener (listener);
194 }
195
196 /**
197 * Perform a generator evaluation.
198 */
199
200 protected void doEvaluate () {
201 if (currentGenerator == null || Thread.currentThread ().isInterrupted ())
202 return;
203 if (currentGenerator instanceof IProgrammableDataGenerator) {
204 IDataGenerator target = (IDataGenerator) currentGenerator.getOption (OPTION_EVALUATIONTARGET);
205 if (target != null) {
206 ((IProgrammableDataGenerator) currentGenerator).getElement (new EvaluationCallStack (target));
207 return;
208 }
209 }
210 currentGenerator.getElement ();
211 }
212
213 /**
214 * Perform a status update following generator evaluation.
215 */
216
217 protected synchronized void doPostUpdate () {
218 if (currentGenerator != null)
219 propertySupport.firePropertyChange (PROPERTY_EVALUATIONUPDATE, generators [position], currentGenerator);
220 if (cancel || position == generators.length)
221 propertySupport.firePropertyChange (PROPERTY_EVALUATIONFINISHED, null, null);
222 else
223 propertySupport.firePropertyChange (PROPERTY_PROGRESS, new Integer (position), new Integer (position + 1));
224 }
225
226 /**
227 * Perform a status update before generator evaluation.
228 */
229
230 protected synchronized void doPreUpdate () {
231 if (processor != null)
232 try {
233 currentGenerator = processor.processGenerator (currentGenerator);
234 } catch (Exception e) {
235 Compare.assertion ("Error encountered while processing generator.", e);
236 }
237 String name = null;
238 if (currentGenerator != null) {
239 name = (String) currentGenerator.getOption (OPTION_EVALUATIONNAME);
240 if (name == null)
241 name = currentGenerator.getName ();
242 }
243 propertySupport.firePropertyChange (PROPERTY_MESSAGE, null, name == null ? "No Value" : name);
244 }
245
246 /**
247 * Evaluates the list of generators.
248 */
249
250 protected void evaluate () {
251 position = 0;
252 Runnable preUpdate = ProxyBuilder.proxyRunnable (this, "doPreUpdate");
253 Runnable postUpdate = ProxyBuilder.proxyRunnable (this, "doPostUpdate");
254 for (int l = generators.length; position < l && !cancel; position++) {
255 currentGenerator = generators [position];
256 try {
257 EventQueue.invokeAndWait (preUpdate);
258 } catch (Exception e) {
259 Compare.assertion ("Unable to update display before evaluation.", e);
260 }
261 try {
262 doEvaluate ();
263 } catch (Exception e) {
264 Compare.assertion ("Error encoutnered while evaluating generator.", e);
265 }
266 try {
267 EventQueue.invokeAndWait (postUpdate);
268 } catch (Exception e) {
269 Compare.assertion ("Unable to update display after evaluation.", e);
270 }
271 }
272 currentGenerator = null;
273 doPostUpdate ();
274 synchronized (this) {
275 executor = null;
276 }
277 }
278 }