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();
}
}