package lawOfDemeter;

import org.aspectj.lang.*;
import java.util.*;

/**
 * The BinStack aspect is part of a Law of Demeter Checker - a set of
 * classes and aspects that verify a given program does not violate
 * the Law of Demeter.  This aspect addresses the concern of storing a stack of
 * bins that contain the targets being examined
 *
 * @author David H. Lorenz
 * @author Modifications made by Paul Freeman
 */
abstract aspect TargetBinStack extends Supplier {

  /**
   * Used in debugging print statements.
   * @return Returns a string describing what this bin contains.
   */
  abstract String contains();

  /**
   * This is the pointcut used by the before and after advice to stack the
   * target bins.
   */
  abstract pointcut EventToStack();

  /**
   * This is the pointcut used by the around advice to gather the objects.
   */
  abstract pointcut EventToGather();

  /**
   * The method that fires to the implementing aspect before the EventToGather
   * proceeds, signalling gathering within the implementing aspect to take place.
   */
  abstract void gatherBefore(JoinPoint thisJoinPoint);

  /**
   * The method that fires to the implementing aspect before the EventToGather
   * proceeds, signalling gathering within the implementing aspect to take place.
   */
  abstract void gatherAfter(JoinPoint thisJoinPoint, Object returnValue);

  /**
   * Advice that maintains a stack of targets.
   */
  Stack targetStack = new Stack();
  Object around():  // keep track of arguments
    EventToStack(){
      // before a method executes, we store the current stack of targets
      targetStack.push(targets);
      // and initialize a new stack of targets
      targets = new HashMap();
      Object returnVal = proceed();
      // after the method executes, we restore the previous stack of targets
      targets = (HashMap)targetStack.pop();
      return returnVal;
  }

  /**
   * Fires the gather method to the implementing aspect.  The advice must occur
   * here in the implementing class so that it can be ordered correctly with the
   * gathering advice.  If the gathering advice was implemented in the subclass,
   * it would fire before the super classes advice.  The dominates keyword will not
   * override this.
   */
  Object around():
    EventToGather(){
      gatherBefore(thisJoinPoint);
      Object returnValue = proceed();
      gatherAfter(thisJoinPoint, returnValue);
      return returnValue;
  }

  /**
   * Prints the objects in the current bin.
   * Used for debugging.
   */
  public void printTopBin(){
    System.out.println("== TOP "+contains()+" ==");
    printHashMap(targets);
    System.out.println("======================");
  }

  /**
   * Prints each bin in the stack.
   * Used for debugging.
   */
  public void printAllBins(){
    System.out.println("== ALL "+contains()+" ==");
    // targetStack contains one empty bin by default so start at size - 1
    for(int i = targetStack.size() - 1; i >= 0; i--){
      System.out.println("-- bin " + i + " --");
      if(i == 0){printHashMap(targets);}
      else{printHashMap((HashMap)targetStack.elementAt(i - 1));}
      System.out.println("-------------------");
    }
    System.out.println("======================");
  }


}