package fi.utu.ville.exercises.helpers; import java.io.Serializable; import java.util.HashSet; import com.vaadin.ui.Component; import fi.utu.ville.exercises.model.ExecutionState; import fi.utu.ville.exercises.model.ExecutionStateChangeListener; import fi.utu.ville.exercises.model.Executor; import fi.utu.ville.exercises.model.ResetListener; import fi.utu.ville.exercises.model.SubmissionInfo; import fi.utu.ville.exercises.model.SubmissionListener; import fi.utu.ville.exercises.model.SubmissionResult; import fi.utu.ville.exercises.model.SubmissionType; /** * A helper-class to which a lot of functionality of an {@link Executor} -implementor can be delegated to. This includes handling {@link ExecutionState}, * {@link ExecutionStateChangeListener}s and {@link SubmissionListener}s. * * @author Riku Haavisto * * @param <S> * {@link SubmissionInfo}-implementor */ public class ExerciseExecutionHelper<S extends SubmissionInfo> implements Serializable { /** * */ private static final long serialVersionUID = -8263504176402794874L; private final HashSet<SubmissionListener<S>> submitListeners = new HashSet<SubmissionListener<S>>(); private final HashSet<ResetListener> resetListeners = new HashSet<ResetListener>(); private final HashSet<ExecutionStateChangeListener> stateListeners = new HashSet<ExecutionStateChangeListener>(); private final MutableExecutionState stateHelper = new MutableExecutionState(); private long startTime; /** * Constructs a new {@link ExerciseExecutionHelper} instance and records start-timestamp. */ public ExerciseExecutionHelper() { startTime = System.currentTimeMillis(); } /** * Registers a {@link SubmissionListener}. {@link Executor #registerSubmitListener(SubmissionListener)} can be delegated to this method. * * @param submitListener * {@link SubmissionListener} to be registered */ public void registerSubmitListener(SubmissionListener<S> submitListener) { submitListeners.add(submitListener); } public void registerResetListener(ResetListener resetListener) { resetListeners.add(resetListener); } /** * A quite standard {@link ExecutionState}-change after reset. Enables reset and submit, and informs registered {@link ExecutionStateChangeListener}s. */ public void informResetDefault() { stateHelper.setCanReset(true); stateHelper.setCanSubmit(true); informStateListeners(); for (ResetListener rListener : resetListeners) { rListener.resetted(); } } /** * Returns currently spent timeOnTask and resets the counter if the {@link SubmissionType} used is not "only for saving state". * * @param submType * {@link SubmissionType} used * @return timeOnTask currently spent time-on-task (seconds) */ private int getTimeOnTask(SubmissionType submType) { int timeOnTask = (int) (System.currentTimeMillis() - startTime) / 1000; if (!submType.isOnlyForSavingState()) { startTime = System.currentTimeMillis(); } return timeOnTask; } /** * Informs registered {@link SubmissionListener}s with {@link SubmissionResult} constructed from the parameters, and performs a quite standard * {@link ExecutionState}-change (disable submit, enable reset) and informs registered {@link ExecutionStateChangeListener}s. * * @param correctness * double presenting correctness of submission in range 0.0 - 1.0 * @param data * {@link SubmissionInfo}-object about the submission * @param submType * used {@link SubmissionType} * @param fbComponent * possible {@link Component} shown as feedback to the user or null */ public void informSubmitDefault(double correctness, S data, SubmissionType submType, Component fbComponent) { int timeOnTask = getTimeOnTask(submType); for (SubmissionListener<S> sListener : submitListeners) { sListener.submitted(new SubmissionResult<S>(correctness, timeOnTask, data, fbComponent, submType)); } stateHelper.setCanReset(true); stateHelper.setCanSubmit(false); informStateListeners(); } /** * <p> * Informs registered {@link SubmissionListener}s with {@link SubmissionResult} constructed from the parameters. * </p> * <p> * Does not perform any {@link ExecutionState}-changes. * </p> * * @param correctness * double presenting correctness of submission in range 0.0 - 1.0 * @param data * {@link SubmissionInfo}-object about the submission * @param submType * used {@link SubmissionType} * @param fbComponent * possible {@link Component} shown as feedback to the user or null */ public void informOnlySubmit(double correctness, S data, SubmissionType submType, Component fbComponent) { int timeOnTask = getTimeOnTask(submType); for (SubmissionListener<S> sListener : submitListeners) { sListener.submitted(new SubmissionResult<S>(correctness, timeOnTask, data, fbComponent, submType)); } } /** * <p> * Informs registered {@link SubmissionListener}s with given {@link SubmissionResult}. * </p> * <p> * Does not perform any {@link ExecutionState}-changes. * </p> * * @param fb * {@link SubmissionResult} to use */ public void informOnlySubmit(SubmissionResult<S> fb) { for (SubmissionListener<S> sListener : submitListeners) { sListener.submitted(fb); } } /** * Informs all the registered {@link ExecutionStateChangeListener}s. Call this after making changes to {@link ExecutionState} that can be accessed through * {@link #getState()} to prompt the listeners to act upon the changes. */ public void informStateListeners() { for (ExecutionStateChangeListener eStateList : stateListeners) { eStateList.actOnStateChange(stateHelper); } } /** * Registers a {@link ExecutionStateChangeListener}. Implementing method * {@link Executor #registerExecutionStateChangeListener(ExecutionStateChangeListener)} can be delegated to this method. * * @param execStateListener * {@link ExecutionStateChangeListener} to be registered */ public void registerExerciseExecutionStateListener( ExecutionStateChangeListener execStateListener) { stateListeners.add(execStateListener); } /** * Returns the current {@link ExecutionState}. {@link Executor #getCurrentExecutionState()} can be delegated to this method. * * @return current {@link ExecutionState} */ public MutableExecutionState getState() { return stateHelper; } }