package osp; import java.awt.geom.Point2D; import java.util.ArrayDeque; import java.util.InputMismatchException; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Observable; import java.util.Scanner; import jsky.coords.DMS; import jsky.coords.HMS; /** * Enumeration of the possible actions, corresponding to the method to call when undoing.
* It is actually the inverse action of the one to be undone, even if most of them are their own inverse * (for example, the inverse operation of setCenter(newCenter) would be setCenter(oldCenter)).
* Only joinSlits/splitSlits differ in that. *

* Each operation has an associated value, needed to set back to the original state. * All those actions concerned only mask and slits operations. * They are detailed here. * @see MaskHistory */ enum Actions { // Mask actions /** * corresponding value: the old center of the mask, as a java.awt.geom.Point2D.Double object * (with pixel coordinates). */ setCenter, /** * corresponding value: the old center (in degrees) of the mask, as a java.awt.geom.Point2D.Double object * (with world degree coordinates). */ setCenterDeg, /** * corresponding value: the old center (in world Coords) of the mask, as a java.awt.geom.Point2D.Double object * (with world coordinates). */ setCenterWc, /** * corresponding value: the old alpha of the mask, as an jsky.coords.HMS object. */ setAlpha, /** * corresponding value: the old delta of the mask, as a jsky.coords.DMS object. */ setDelta, /** * corresponding value: the old omega of the mask, as a java.lang.Double object. */ setOmega, // Unique slit actions /** * corresponding value: the number of the object previously affected to the slit, * as a java.lang.Integer object. */ setAffectedObject, /** * corresponding value: the old position of the slit, as a java.lang.Double object. */ setPosition, /** * corresponding value: the old aperture of the slit, as a java.lang.Double object. */ setAperture, // Multiple slits actions /** * corresponding value: none, pass null as a value. */ joinSlits, /** * corresponding value: none, pass null as a value. */ splitSlits } /** * This class represents an history for a given mask. * It contains a list of entries, corresponding to the different operations made on the mask, in the order they were done. * They can be undone, one by one, by calling the undo() method, which undoes the last operation made on the mask * or on the slit. * @see Actions * @see MaskHistoryEntry */ public class MaskHistory extends Observable { /** * The parent mask */ private Mask mask; /** * The list of entries. */ private ArrayDeque entries; /** * Constructs a new history for the given mask, with no entries in it. * @param m The mask this history is attached to. */ public MaskHistory( Mask m ) { mask = m; this.addObserver(mask); entries = new ArrayDeque(); } /** * Undoes the last entry, corresponding to a list of actions done on the mask that are * reverted to the values before the modification. * * @throws NoSuchElementException If there is no entry in the history. */ public void undo() throws NoSuchElementException { MaskHistoryEntry lastEntry = entries.pop(); Scanner scan; // Iterates through all the actions present in the last entry and executes them for ( Iterator actions = lastEntry.getActions().iterator(); actions.hasNext(); ) { String action = actions.next(); // System.out.println("MaskHist "+action ); scan = new Scanner( action ); switch ( Actions.valueOf( scan.next() ) ) { case setCenter: mask.setCenter( (Point2D.Double) lastEntry.getValue(action) ); break; case setCenterDeg: mask.setCenterDeg( (Point2D.Double) lastEntry.getValue(action) ); break; case setCenterWc: Point2D.Double pt=(Point2D.Double) lastEntry.getValue(action); mask.setCenterWc(pt.getX(),pt.getY() ); break; case setAlpha: mask.setAlpha( (HMS) lastEntry.getValue(action) ); break; case setDelta: mask.setDelta( (DMS) lastEntry.getValue(action) ); break; case setOmega: Double omega = (Double) lastEntry.getValue(action); mask.setOmega( omega.doubleValue() ); break; case setAffectedObject: int slitNum = scan.nextInt(); Integer affectedObject = (Integer) lastEntry.getValue(action); mask.getSlit( slitNum ).setAffectedObject( affectedObject.intValue() ); if ( affectedObject.intValue() < 0 ) mask.setPlotSpectrum(slitNum, false); break; case setPosition: Double position = (Double) lastEntry.getValue(action); mask.getSlit( scan.nextInt() ).setPosition( position.doubleValue() ); break; case setAperture: Double aperture = (Double) lastEntry.getValue(action); mask.getSlit( scan.nextInt() ).setAperture( aperture.doubleValue() ); break; case joinSlits: int startSlit = scan.nextInt(); mask.joinSlits( startSlit, scan.nextInt(), startSlit); break; case splitSlits: int firstSlit = scan.nextInt(); int lastSlit = scan.nextInt(); mask.splitSlits( firstSlit, lastSlit ); break; } // End switch scan.close(); } // End for } /** * Creates and adds a new (empty) entry to the history. * @param name Name of the entry. */ public void addEntry(String name) { if ( mask.imagePater.searching && name != "Search" ) return; entries.push( new MaskHistoryEntry(name) ); notifyObservers(); } /** * Adds an action to the last entry. It must be one of those listed in the enumeration Actions. * The corresponding value for each action is also specified in the enumeration. *

* If the action is on a slit, the number of the slit must be put after the action, in the string, * separated with a whitespace.
* If the action is on two slits, then the second number must be added in the same fashion after the first. * * @param action The action to do, with eventually a (or two) slit(s) number(s). * @param value The value that has been changed and has to be restored when undoing. * @throws IllegalArgumentException If the action is not correct. * @throws InputMismatchException If the action is on a(two) slit(s) and doesn't give the number(s) of the slit(s) correctly. */ public void addAction(String action, Object value) throws IllegalArgumentException, InputMismatchException { if ( action.isEmpty() || (mask.imagePater.searching && action != "deleteAllMasks") ) return; // Checks if the action is valid Scanner scan = new Scanner(action); Actions.valueOf( scan.next() ); boolean slitsInvalid = false; if ( scan.hasNext() ) { int slitNum = scan.nextInt(); if (slitNum < 0 || slitNum >= Mask._NBRSLITS_) slitsInvalid = true; if ( scan.hasNext() ) slitNum = scan.nextInt(); if (slitNum < 0 || slitNum >= Mask._NBRSLITS_) slitsInvalid = true; } scan.close(); if (slitsInvalid) throw new InputMismatchException(); entries.peek().putAction(action, value); } /** * Get the last entry put in the history. * @return The last entry present in the history. Returns null if no entry is present. */ public MaskHistoryEntry getLastEntry() { return entries.peek(); } /** * Deletes the last entry of the history. * @throws NoSuchElementException If there's no entry in the history. */ public void deleteLastEntry() throws NoSuchElementException { entries.pop(); } /** * Tests if the history is empty (no entries in it). * @return true if the history has no entries. */ public boolean isEmpty() { return entries.isEmpty(); } }