import java.util.*; import java.lang.reflect.*; import org.aspectj.lang.annotation.*; 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 B ChildHasParent.parent; public E ChildHasParent.getParent() { return parent; } public List ParentHasChildren.getChildren() { return Collections.unmodifiableList(children); } public void ChildHasParent.setParent(F parent) { parent.addChild(this); } public void ParentHasChildren.addChild(G child) { if (child.getParent() != null) { child.getParent().removeChild(child); } children.add(child); child.parent = this; } public void ParentHasChildren.removeChild(H child) { if (children.remove(child)) { child.parent = null; } } @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); } aspect GenericAspectW 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 TestV removed the casts (wow!) TestW promotes the pointcuts and few slight changes to the implementations to bring it in line with whats in the AJDK */