MaskHistory.java 8.22 KB
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.<br/>
 * 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)).<br/>
 * Only <i>joinSlits</i>/<i>splitSlits</i> differ in that.
 * <br/><br/>
 * 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 <a href="#undo()">undo()</a> 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<MaskHistoryEntry> 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<MaskHistoryEntry>();
  }
  
  /**
   * 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<String> 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 <a href="./Actions.html">Actions</a>.
   * The corresponding value for each action is also specified in the enumeration.
   * <br/><br/>
   * 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.<br/>
   * 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();
  }
}