package com.google.code.joto.ast.accesspath; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import com.google.code.joto.reflect.ReflectUtils; /** * root class hierarchy of AST for Object Access Path traversal * * * built-in Access Path: * Object --Field--> Object/Primitive * Class --StaticField--> Object/Primitive * ObjectArray --[index]--> ArrayElt Object/Primitive * ObjectArray --.length--> primitive ArrayLength * * non-built-in AccessPath: (cf implementation using Array / LinkedList...) * Collection --.get(index)/.add()/.remove()/.iterator()--> Object CollectionElt * Map[key] --.get(key)/.put()/.remove()/.iterator()--> Object Map.Entry value * * CompountPath --elt1 o elt2 o ... o eltN--> Object * * AggregateField: Object --Field1 o Field2--> Object/Primitive * AggregateObjectArray: Object --Field o [index]--> Object/Primitive * AggregateCollection: Object --Field o .get(index)/.add()/.remove()/.iterator()--> Object * AggregateMap: Object --Field o .get(key)/.put()/.remove()/.iterator()--> Object * * Inverse Relation Object: child-->implicit parent : Object--add/remove(child)--> .... side effect: child.setParent(object) * * Note: * non-built-in PathElt can be partially accessible, with .add() only and not .get() ... * exemple in JavaBeans addListener()/removeListener() ... but no getListener() !! * * */ public abstract class AccessPathAST { protected Class<?> lhsType; protected Class<?> resultType; // ------------------------------------------------------------------------- protected AccessPathAST(Class<?> lhsType, Class<?> resultType) { this.lhsType = lhsType; this.resultType = resultType; } public abstract void visit(AccessPathVisitor v); // ------------------------------------------------------------------------- protected static class PropertyMethods { private Method readMethod; private Method writeMethod; public PropertyMethods(Class<?> clss, Class<?> fieldType, String propName) { this.readMethod = ReflectUtils.findMethod(clss, "get" + propName); if (readMethod == null && fieldType.equals(boolean.class)){ readMethod = ReflectUtils.findMethod(clss, "is" + propName); } this.writeMethod = ReflectUtils.findMethod(clss, "set" + propName, fieldType); } public Method getReadMethod() { return readMethod; } public Method getWriteMethod() { return writeMethod; } public Method getPublicReadMethod() { return ReflectUtils.methodIfPublic(readMethod); } public Method getPublicWriteMethod() { return ReflectUtils.methodIfPublic(writeMethod); } } /** * */ public static abstract class AbstractFieldPathElt extends AccessPathAST { private final Field field; private PropertyMethods methods; public AbstractFieldPathElt(Field field) { super(field.getDeclaringClass(), field.getType()); this.field = field; String propName = ReflectUtils.fieldToCapitalizedName(field); this.methods = new PropertyMethods(field.getDeclaringClass(), field.getType(), propName); } public Field getField() { return field; } public String getFieldName() { return field.getName(); } public PropertyMethods getMethods() { return methods; } public Method getReadMethod() { return methods.getReadMethod(); } public Method getWriteMethod() { return methods.getWriteMethod(); } public Method getPublicReadMethod() { return methods.getPublicReadMethod(); } public Method getPublicWriteMethod() { return methods.getPublicWriteMethod(); } } /** * */ public static class ObjectFieldAccess extends AbstractFieldPathElt { public ObjectFieldAccess(Field field) { super(field); } public void visit(AccessPathVisitor v) { v.caseObjectField(this); } } /** * */ public static class ClassFieldAccess extends AbstractFieldPathElt { public ClassFieldAccess(Field field) { super(field); } public void visit(AccessPathVisitor v) { v.caseClassStaticField(this); } } /** * AccessPathAST sub-class for Object[] ---index---> ArrayElt */ public static class ArrayIndexAccess extends AccessPathAST { public ArrayIndexAccess(Class<?> lhsArrayType) { super(lhsArrayType, lhsArrayType.getComponentType()); } public void visit(AccessPathVisitor v) { v.caseArrayIndex(this); } } // Non built-in AccessPath, for standard java.util. Collection / Map // ------------------------------------------------------------------------- public static class CollectionMethods { private Method iteratorMethod; private Method getIndexedMethod; private Method addMethod; // private Method addIndexedMethod; // private Method setIndexedMethod; // private Method removeMethod; public CollectionMethods(Class<?> clss) { this.iteratorMethod = ReflectUtils.findMethod(clss, "iterator"); this.getIndexedMethod = ReflectUtils.findMethod(clss, "get", int.class); this.addMethod = ReflectUtils.findMethod(clss, "add"); } public Method getIteratorMethod() { return iteratorMethod; } public Method getGetIndexedMethod() { return getIndexedMethod; } public Method getAddMethod() { return addMethod; } } /** * */ public static class CollectionAccess extends AccessPathAST { private CollectionMethods methods; public CollectionAccess(Class<?> collectionClass, Class<?> eltType) { super(collectionClass, eltType); this.methods = new CollectionMethods(collectionClass); } public void visit(AccessPathVisitor v) { v.caseCollectionAccess(this); } public CollectionMethods getMethods() { return methods; } } // ------------------------------------------------------------------------- public static class MapMethods { private Method entrySetMethod; private Method valuesMethod; private Method getMethod; private Method putMethod; public MapMethods(Class<?> clss) { this.entrySetMethod = ReflectUtils.findMethod(clss, "entrySet"); this.valuesMethod = ReflectUtils.findMethod(clss, "values"); this.getMethod = ReflectUtils.findMethod(clss, "get", Object.class); this.putMethod = ReflectUtils.findMethod(clss, "put", Object.class, Object.class); } public Method getEntrySetMethod() { return entrySetMethod; } public Method getValuesMethod() { return valuesMethod; } public Method getGetMethod() { return getMethod; } public Method getPutMethod() { return putMethod; } } /** * */ public static class MapAccess extends AccessPathAST { private MapMethods methods; public MapAccess(Class<?> collectionClass, Class<?> eltType) { super(collectionClass, eltType); this.methods = new MapMethods(collectionClass); } public void visit(AccessPathVisitor v) { v.caseMapAccess(this); } public MapMethods getMethods() { return methods; } } // Compound design pattern: path = pathElt1/pathElt2/...pathEltN // ------------------------------------------------------------------------- /** * */ public static class CompoundPathAccess extends AccessPathAST { private List<AccessPathAST> pathElts = new ArrayList<AccessPathAST>(); public CompoundPathAccess(Class<?> lhsType, Class<?> resultType, AccessPathAST... pathElts) { this(lhsType, resultType, Arrays.asList(pathElts)); } public CompoundPathAccess(Class<?> lhsType, Class<?> resultType, List<AccessPathAST> pathElts) { super(lhsType, resultType); this.pathElts.addAll(pathElts); } @Override public void visit(AccessPathVisitor v) { v.caseCompoundPath(this); } public List<AccessPathAST> getPathElts() { return pathElts; } } // Aggregate Partial Field Path // used for example as SCC objects : Secundary Citizen Class // ------------------------------------------------------------------------- /** * */ public static class AggregateFieldFieldAccess extends AccessPathAST { private ObjectFieldAccess parentFieldAccess; private ObjectFieldAccess childFieldAccess; private String aggrFieldName; private PropertyMethods methods; public AggregateFieldFieldAccess( ObjectFieldAccess parentFieldAccess, ObjectFieldAccess childFieldAccess ) { super(parentFieldAccess.lhsType, childFieldAccess.resultType); // extends ObjectFieldAccess + override init for finding/hidding partial ?? // ... or extends AccessPathAST + delegate... this.parentFieldAccess = parentFieldAccess; this.childFieldAccess = childFieldAccess; this.aggrFieldName = ReflectUtils.fieldToCapitalizedName(parentFieldAccess.getFieldName()) + ReflectUtils.fieldToCapitalizedName(childFieldAccess.getFieldName()); this.methods = new PropertyMethods(childFieldAccess.lhsType, childFieldAccess.resultType, aggrFieldName); } @Override public void visit(AccessPathVisitor v) { v.caseAggrFieldField(this); } public ObjectFieldAccess getParentFieldAccess() { return parentFieldAccess; } public ObjectFieldAccess getChildFieldAccess() { return childFieldAccess; } public String getAggrFieldName() { return aggrFieldName; } public PropertyMethods getMethods() { return methods; } } /** * */ public static class AggregateFieldCollectionAccess extends AccessPathAST { private ObjectFieldAccess parentFieldAccess; private ObjectFieldAccess childFieldAccess; private String aggrFieldName; private CollectionMethods methods; public AggregateFieldCollectionAccess( ObjectFieldAccess parentFieldAccess, ObjectFieldAccess childFieldAccess ) { super(parentFieldAccess.lhsType, childFieldAccess.resultType); // extends ObjectFieldAccess + override init for finding/hidding partial ?? // ... or extends AccessPathAST + delegate... this.parentFieldAccess = parentFieldAccess; this.childFieldAccess = childFieldAccess; this.aggrFieldName = ReflectUtils.fieldToCapitalizedName(parentFieldAccess.getFieldName()) + ReflectUtils.fieldToCapitalizedName(childFieldAccess.getFieldName()); //TODO this.methods = new CollectionMethods(childFieldAccess.lhsType, childFieldAccess.resultType, aggrFieldName); } @Override public void visit(AccessPathVisitor v) { v.caseAggrFieldCollection(this); } public ObjectFieldAccess getParentFieldAccess() { return parentFieldAccess; } public ObjectFieldAccess getChildFieldAccess() { return childFieldAccess; } public String getAggrFieldName() { return aggrFieldName; } public CollectionMethods getMethods() { return methods; } } /** * */ public static class AggregateFieldMapAccess extends AccessPathAST { private ObjectFieldAccess parentFieldAccess; private ObjectFieldAccess childFieldAccess; private String aggrFieldName; private MapMethods methods; public AggregateFieldMapAccess( ObjectFieldAccess parentFieldAccess, ObjectFieldAccess childFieldAccess ) { super(parentFieldAccess.lhsType, childFieldAccess.resultType); // extends ObjectFieldAccess + override init for finding/hidding partial ?? // ... or extends AccessPathAST + delegate... this.parentFieldAccess = parentFieldAccess; this.childFieldAccess = childFieldAccess; this.aggrFieldName = ReflectUtils.fieldToCapitalizedName(parentFieldAccess.getFieldName()) + ReflectUtils.fieldToCapitalizedName(childFieldAccess.getFieldName()); //TODO this.methods = new MapMethods(childFieldAccess.lhsType, childFieldAccess.resultType, aggrFieldName); } @Override public void visit(AccessPathVisitor v) { v.caseAggrFieldMap(this); } public ObjectFieldAccess getParentFieldAccess() { return parentFieldAccess; } public ObjectFieldAccess getChildFieldAccess() { return childFieldAccess; } public String getAggrFieldName() { return aggrFieldName; } public MapMethods getMethods() { return methods; } } }