import java.lang.annotation.*; import java.util.*; import java.lang.reflect.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @interface Relationship{ String inverse(); Class target(); // Necessary because java uses type errasure boolean oneWay() default false; } class CheckRelationships { public static void check(Class c) throws Misspecification{ for(Field f : c.getDeclaredFields()){ Relationship r = f.getAnnotation(Relationship.class); if (r != null) check(f, r); } } private static void check(Field f, Relationship r) throws Misspecification{ if (! r.oneWay() ){ Field otherField; Class other; Relationship otherRelationship; Class me = f.getDeclaringClass(); other = r.target(); if (other == null) throw new Misspecification(f,null,"target is null"); try{ otherField = other.getDeclaredField(r.inverse()); ParameterizedType pt = (ParameterizedType)otherField.getGenericType(); }catch(NoSuchFieldException e){ throw new Misspecification(f, null, "inverse field not existing"); } otherRelationship = otherField.getAnnotation(Relationship.class); if (otherRelationship.target() != me) throw new Misspecification(f,otherField,"target mismatch"); } } public static class Misspecification extends Exception { public Field source, target; Misspecification(Field s, Field t, String error){ super(error); source = s; target = t; } } }