/* Date: 3/22/05
 * File: TargetBinStack.java
 * Modified by:
 *   Joseph Krauze
 *   Chetan Loke
 *
 * Description: The around() advice on the EventToGather() pointcut
 *              was replaced by a before and after since in AspectJ
 *              1.1.1, an around advice cannot be invoked on an object
 *              that is getting initialized.
 */

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 Paul Freeman
 */
abstract aspect TargetBinStack extends Supplier {

  /**
   * 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 assists in maintaining the stack of targets.
   * Adds a new target bin to the top of the stack.
   */
  Stack targetStack = new Stack();
  before():  // 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 IdentityHashMap();
  }

  /**
   * 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.
   */
  /**
   * NOTE: "Dominates" has been replaced by "precedes"
   **/
  /*Object around(): EventToGather(){
    gatherBefore(thisJoinPoint);
    Object returnValue = proceed();
    gatherAfter(thisJoinPoint, returnValue);
    return returnValue;
  }*/
  before() : EventToGather() {
    gatherBefore(thisJoinPoint);
  }
  
  after() returning (Object ret) : EventToGather() {
    gatherAfter(thisJoinPoint, ret);
  }

  /**
   * Advice that assists in maintaining the stack of targets.
   * Removes the target bin from the top of the stack.
   */
  after():
    EventToStack(){
      // after the method executes, we restore the previous stack of targets
      targets = (IdentityHashMap)targetStack.pop();
  }

  /**
   * Prints the objects in the top bin.
   * Overrides the method inherited from supplier.
   * Used for debugging.
   */
   public void printTargets(){
    System.out.println("== TOP "+contains()+" for "+thisObject+" ==");
    printHashMap(targets);
    System.out.println("======================");
  }

  /**
   * Prints each bin in the stack.
   * Used for debugging.
   */
  public void printAllTargets(){
    System.out.println("== ALL "+contains()+" for "+thisObject+" ==");
    // targetStack contains one empty bin by default so don't print element at 0
    for(int i = 0; i < targetStack.size(); i++){
      System.out.println("-- bin " + (targetStack.size() - 1 - i) + " --");
      if(i == 0){printHashMap(targets);}
      else{printHashMap((IdentityHashMap)targetStack.elementAt(i));}
      System.out.println("-------------------");
    }
//    for(int i = targetStack.size() - 1; i >= 0; i--){
//      System.out.println("-- bin " + i + " --");
//      if(i == 0){printHashMap(targets);}
//      else{printHashMap((IdentityHashMap)targetStack.elementAt(i - 1));}
//      System.out.println("-------------------");
//    }
  }


}

