import java.util.*; import java.lang.reflect.*; import org.aspectj.lang.annotation.*; abstract aspect ParentChildRelationship { interface ParentHasChildren{} interface ChildHasParent

{} declare parents: Parent implements ParentHasChildren; declare parents: Child implements ChildHasParent; public List ParentHasChildren.children; public P ChildHasParent

.parent; } aspect GenericAspectP extends ParentChildRelationship { public static void main(String []argv) { // Check the state of top Top t = new Top(); check(t instanceof ParentHasChildren,"Top should implement ParentHasChildren"); Type[] intfs = Top.class.getGenericInterfaces(); check(intfs[0] instanceof ParameterizedType, "Expected Top to have parameterized interface but found "+intfs[0]); ParameterizedType pt = (ParameterizedType) intfs[0]; Type[] tArgs = pt.getActualTypeArguments(); check(tArgs[0]==Bottom.class, "Expecting Bottom parameter but found " + tArgs[0]); // Check the state of top Bottom b = new Bottom(); check(b instanceof ChildHasParent,"Bottom should implement ChildHasParent"); intfs = Bottom.class.getGenericInterfaces(); check(intfs[0] instanceof ParameterizedType, "Expected Bottom to have parameterized interface but found "+intfs[0]); pt = (ParameterizedType) intfs[0]; tArgs = pt.getActualTypeArguments(); check(tArgs[0]==Top.class, "Expecting Top parameter but found " + tArgs[0]); // Field fiddling b.parent = t; List kids = new ArrayList(); kids.add(b); t.children = kids; } public static void check(boolean b,String msg) { if (!b) throw new RuntimeException(msg); } } class Top {} class Bottom {} ////////////////////////////////////////////////////////////////// /* End game for test Z, as bits work they are promoted up into the testcase above :) TestN promoted the declare parents statements up TestO promoted the fields up - a parent knows its children, a child knows its parents - but then used them incorrectly TestP uses the fields correctly public abstract aspect ParentChildRelationship { public List ParentHasChildren.getChildren() { return Collections.unmodifiableList(children); } public P ChildHasParent

.getParent() { return parent; } public void ParentHasChildren.addChild(C child) { if (child.parent != null) { child.parent.removeChild(child); } children.add(child); child.parent = this; } public void ParentHasChildren.removeChild(C child) { if (children.remove(child)) { child.parent = null; } } public void ChildHasParent

.setParent(P parent) { parent.addChild(this); } @SuppressAjWarnings public pointcut addingChild(Parent p, Child c) : execution(* Parent.addChild(Child)) && this(p) && args(c); @SuppressAjWarnings public pointcut removingChild(Parent p, Child c) : execution(* Parent.removeChild(Child)) && this(p) && args(c); } */