package net.sf.openrocket.gui.simulation; import java.util.Arrays; import javax.swing.SwingWorker; import net.sf.openrocket.document.Simulation; import net.sf.openrocket.simulation.FlightData; import net.sf.openrocket.simulation.SimulationStatus; import net.sf.openrocket.simulation.exception.SimulationCancelledException; import net.sf.openrocket.simulation.listeners.AbstractSimulationListener; import net.sf.openrocket.simulation.listeners.SimulationListener; /** * A SwingWorker that runs a simulation in a background thread. The simulation * always includes a listener that checks whether this SwingWorked has been cancelled, * and throws a {@link SimulationCancelledException} if it has. This allows the * {@link #cancel(boolean)} method to be used to cancel the simulation. * * @author Sampo Niskanen <sampo.niskanen@iki.fi> */ public abstract class SimulationWorker extends SwingWorker<FlightData, SimulationStatus> { protected final Simulation simulation; private Throwable throwable = null; public SimulationWorker(Simulation sim) { this.simulation = sim; } /** * Runs the simulation. */ @Override protected FlightData doInBackground() { if (isCancelled()) { throwable = new SimulationCancelledException("The simulation was interrupted."); return null; } SimulationListener[] listeners = getExtraListeners(); if (listeners != null) { listeners = Arrays.copyOf(listeners, listeners.length + 1); } else { listeners = new SimulationListener[1]; } listeners[listeners.length - 1] = new CancelListener(); try { simulation.simulate(listeners); } catch (Throwable e) { throwable = e; return null; } return simulation.getSimulatedData(); } /** * Return additional listeners to use during the simulation. The default * implementation returns an empty array. * * @return additional listeners to use, or <code>null</code>. */ protected SimulationListener[] getExtraListeners() { return new SimulationListener[0]; } /** * Called after a simulation is successfully simulated. This method is not * called if the simulation ends in an exception. */ protected abstract void simulationDone(); /** * Called if the simulation is interrupted due to an exception. * * @param t the Throwable that caused the interruption */ protected abstract void simulationInterrupted(Throwable t); /** * Marks this simulation as done and calls the progress update. */ @Override protected final void done() { if (throwable == null) simulationDone(); else simulationInterrupted(throwable); } /** * A simulation listener that throws a {@link SimulationCancelledException} if * this SwingWorker has been cancelled. The conditions is checked every time a step * is taken. * * @author Sampo Niskanen <sampo.niskanen@iki.fi> */ private class CancelListener extends AbstractSimulationListener { @Override public void postStep(SimulationStatus status) throws SimulationCancelledException { if (isCancelled()) { throw new SimulationCancelledException("The simulation was interrupted."); } } } }