// difference between call and execution

class Speaker { }
class Lecturer extends Speaker { }
class ArrogantLecturer extends Lecturer { }

aspect MainBehavior {
  // (define-msg say)
  void Speaker.say(Object stuff) { }

  // (define-method say ((arrogant-lecturer? x) stuff) ...)
  // This has to come first so that it takes precedence when it
  // overlaps with the next around advice!
  void around(ArrogantLecturer x, Object stuff)
    : execution(* say(*)) && target(x) && args(stuff) {
    System.out.println(" around(ArrogantLecturer x, Object stuff)");
    System.out.println(thisJoinPoint.toLongString());
    System.out.println(thisJoinPoint.getKind());
    System.out.println("Target = " + thisJoinPoint.getTarget());
    System.out.println(thisJoinPoint.getThis());
    proceed(x, "it is obvious that " + stuff);
  }

  // (define-method say ((speaker? x) stuff) ...)
  void around(Speaker x, Object stuff)
    : execution(* say(*)) && target(x) && args(stuff) {
    System.out.println(" around(Speaker x, Object stuff)");
    System.out.println(thisJoinPoint.toLongString());
    System.out.println(thisJoinPoint.getKind());
    System.out.println("Target = " + thisJoinPoint.getTarget());
    System.out.println(thisJoinPoint.getThis());
    System.out.println(stuff);
  }

  // (define-msg lecture)
  void Lecturer.lecture(Object stuff) { }

  // (define-method lecture ((lecturer? x) stuff) ...)
  void around(Lecturer x, Object stuff)
    : execution(* lecture(*)) && target(x) && args(stuff) {
    x.say(stuff);
    x.say("you should be taking notes");
  }
}

public class Main {
  public static void main(String args[]) {
    Speaker George = new Speaker();
    George.say("the sky is blue");
    System.out.println("----------- done Speaker George.say");
    Lecturer Gerry = new Lecturer();
    Gerry.say("the sky is blue");
    System.out.println("----------- done Lecturer Gerry.say");
    Gerry.lecture("the sky is blue");
    System.out.println("----------- done Lecturer Gerry.lecture");
    ArrogantLecturer Albert = new ArrogantLecturer();
    Albert.say("the sky is blue");
    System.out.println("----------- done ArrogantLecturer Albert.say");
    Albert.lecture("the sky is blue");
    System.out.println("----------- done ArroganLecturer Albert.lecture");
  }
}

/* output:
the sky is blue
----------- done Speaker George.say
the sky is blue
----------- done Lecturer Gerry.say
the sky is blue
you should be taking notes
----------- done Lecturer Gerry.lecture
it is obvious that the sky is blue
----------- done ArrogantLecturer Albert.say
it is obvious that the sky is blue
it is obvious that you should be taking notes
----------- done ArroganLecturer Albert.lecture
*/