import java.util.*; import java.lang.reflect.*; import org.aspectj.lang.annotation.*; // JUST DONT ASK HOW THIS WORKS abstract aspect ParentChildRelationship { interface ParentHasChildren{ List getChildren(); void addChild(C child); void removeChild(C child); } interface ChildHasParent

{ P getParent(); void setParent(P parent); } declare parents: Parent implements ParentHasChildren; declare parents: Child implements ChildHasParent; public List ParentHasChildren.children = new ArrayList(); public P ChildHasParent

.parent; public List ParentHasChildren.getChildren() { return Collections.unmodifiableList(children); } public P ChildHasParent

.getParent() { return parent; } public void ChildHasParent.setParent(R parent) { this.parent = parent; ((ParentHasChildren)parent).addChild(this); } public void ParentHasChildren.addChild(X child) { if (((ChildHasParent)child).parent != null) { ((ParentHasChildren)((ChildHasParent)child).parent).removeChild(child); } children.add(child); } public void ParentHasChildren.removeChild(Y child) { if (children.remove(child)) { ((ChildHasParent)child).parent = null; } } } aspect GenericAspectU 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; // start using the methods List kids2 = t.getChildren(); check(kids2.size()==1, "Expected one child of the Top but found "+kids2.size()); check(kids2.get(0).equals(b), "Expected one child of the Top which was what we put in there!"+kids2.get(0)); // and the parent methods Top retrievedParent = b.getParent(); check(retrievedParent==t, "parent check 1 failed "+ "retrieved="+retrievedParent+" expected="+t); Top top2 = new Top(); b.setParent(top2); Top retrievedParent2 = b.getParent(); check(retrievedParent2==top2, "parent check 2 failed "+ "retrieved="+retrievedParent2+" expected="+top2); Top top3 = new Top(); Bottom bot2 = new Bottom(); top3.addChild(bot2); Bottom aBottom = top3.getChildren().get(0); check(aBottom==bot2,"Incorrect child? expected="+bot2+" found="+aBottom); top3.removeChild(bot2); int size=top3.getChildren().size(); check(size==0,"Should be no children but there were "+size); } 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 TestQ ... tests some stumbling blocks I encountered before R... TestR promoted getChildren() method TestS promoted getParent() and setParent() TestT ... tests some stumbling blocks I encountered before U... TestU promoted addChild and removeChild public abstract aspect ParentChildRelationship { @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); } */