import org.aspectj.testing.Tester; 
import org.aspectj.testing.Tester;

public class IntroduceInnerInterfaceCP {
    public static void main(String[] args) {
        new TargetClass().walk();
        new TargetClassWithoutImplementation().run();
        AnotherClass.invoke();
        Tester.checkAllEvents();
    }
    private static final String[] EXPECTED;
    static {
        EXPECTED = new String[] 
        { "walk", "Implementation.walk", 
          "execution(void TargetClass.walk())",
          "AnotherClass.invoke()", 
          "Protected execution(void AnotherClass.invoke())" 
        };
        Tester.expectEventsInString(EXPECTED);
    }
}
class Signal {
    public static final void signal(String s) {
        //System.err.println(s);
        Tester.event(s);
    }
}
class TargetClass {
    public void walk() { 
        Signal.signal("walk");
    }
}

class TargetClassWithoutImplementation {
    void run() { }
}

aspect Aspect {
    /** @testcase PR#494 adding private interface inside aspect */
    private interface Inner {
        // automagically interpreted as public
        void walk();
    }
    /** @testcase PR#494 using private interface inside aspect for introductions */
    declare parents
        : TargetClass implements Inner;
    declare parents
        : TargetClassWithoutImplementation implements Inner;
    declare parents
        : TargetClassWithoutImplementation extends Implementation;
    static class Implementation {
        public void walk() { 
            Signal.signal("Implementation.walk");
        }

    }
    /** @testcase PR#494 using private interface inside aspect in advice */
    before(TargetClassWithoutImplementation t) : target(t)
        && execution(void TargetClassWithoutImplementation.run()) {
        ((Inner) t).walk();
    }

    /** @testcase PR#494 using private interface inside aspect in execution pcd */
    before() : execution(public void Inner.*()) {
        // validate that interface implemented - addressable in pcd
        Signal.signal(thisJoinPointStaticPart.toString());
    }
}

class AnotherClass {
    static void invoke() {
        Signal.signal("AnotherClass.invoke()");
    }
}
abstract aspect AbstractAspect {
    /** Protected has no join points - validate with ShowToChild before advice */
    protected interface Protected {}
}
aspect ShowToChild extends AbstractAspect {
    /** @testcase PR#494 compile should bind protected interface name in aspect subclass for introduction */
    declare parents : AnotherClass implements Protected;              
    /** @testcase PR#494 compile should bind protected interface name in aspect subclass for advice (even when binding static, non-interface methods with tag interfaces)  */
    after () : within(Protected+) && execution(* *(..)) {
        Signal.signal("Protected " + thisJoinPointStaticPart.toString());
    }
    /** Protected has no join points */
    before () : within(Protected) {
        Tester.checkFailed("within Protected");
    }
}