package lawOfDemeter;

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

/**
 * The Checker 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 verifies that method calls do not
 * violate the Law of Demeter.
 *
 * @author David H. Lorenz
 * @author Modifications made by Paul Freeman
 */

public aspect Checker {

  /**
   * Prints a violation noitice using the thisJoinPoint argument.
   * Also provides a method that can be captured using the included pointcut
   * Checker.Violation().
   *
   * @param thisJoinPoint
   */
  pointcut Violation(JoinPoint tjp):
    execution(private void Checker.raiseViolation(JoinPoint)) && args(tjp);
  private void raiseViolation(JoinPoint thisJoinPoint){
    System.out.println("!! LoD violation: "+thisJoinPoint+JPUtil.at(thisJoinPoint));
    // used for debugging
//    printSuppliers();
  }

  /**
   * Prints a success noitice using the thisJoinPoint argument.
   * Also provides a method that can be captured using the included pointcut
   * Checker.Success().
   *
   * @param thisJoinPoint
   * @param binsIn
   */
  pointcut Success(JoinPoint tjp, Vector binsIn):
    execution(private void Checker.raiseSuccess(JoinPoint, Vector)) && args(tjp, binsIn);
  private void raiseSuccess(JoinPoint thisJoinPoint, Vector binsIn){
    // used for debugging
//    if(binsIn.size() > 1){
//    System.out.println("Call OK ("+thisJoinPoint+")"+JPUtil.at(thisJoinPoint)+
//      " target found in " + binsIn);
//    }
//    printSuppliers(binsIn);
  }

  /**
   * Advice that does the checking.
   */
  before():
    Any.ToCheck(){
      Object thisObj = thisJoinPoint.getThis();
      Object target = thisJoinPoint.getTarget();
      Vector binsIn = new Vector();

      // used for debugging
//      System.out.println("Checking--" + thisJoinPoint + JPUtil.at(thisJoinPoint));
//      printSuppliers();

      //check if target is in any of the available supplier bins
      Vector suppliers = Supplier.getSuppliers();
      Iterator supIt = suppliers.iterator();
      while(supIt.hasNext()){
        Supplier thisBin = (Supplier)supIt.next();
        if(thisBin.contains(target)){
          binsIn.add(thisBin);
        }
      }

      if (binsIn.size() <= 0){
      // if the target is not a preferred supplier -> Violation
        raiseViolation(thisJoinPoint);
      }else{
        raiseSuccess(thisJoinPoint, binsIn);
      }
  }

  // used for debugging
  private void printSuppliers(){
    Vector suppliers = Supplier.getSuppliers();
    printSuppliers(suppliers);
  }

  // used for debugging
  private void printSuppliers(Vector suppliers){
      Iterator supIt = suppliers.iterator();
      while(supIt.hasNext()){
        Supplier thisBin = (Supplier)supIt.next();
        // used for debugging
        if(thisBin instanceof TargetBinStack){
          ((TargetBinStack)thisBin).printAllTargets();
        }else{
          thisBin.printTargets();
        }
      }
  }

}
