001 package org.springframework.richclient.widget.table; 002 003 import java.lang.reflect.InvocationTargetException; 004 import java.lang.reflect.Method; 005 006 /** 007 * This {@link Accessor} uses a chaining implementation of getter methods to allow nested properties. 008 * 009 * @author Jan Hoskens 010 * @since 0.5.0 011 */ 012 public class NestedAccessor implements Accessor 013 { 014 015 /** Lazily created accessor to access the nested property on the top level property object. */ 016 private Accessor wrappedAccessor; 017 018 /** 019 * The nested property. Will be used to create an accessor together with the return type of the top level 020 * property object. 021 */ 022 final private String nestedProperty; 023 024 /** Getter method to access the top level property object. */ 025 final private Method getter; 026 027 /** 028 * Convenience constructor. Creates a getter method for the given class and property and reroutes to 029 * {@link NestedAccessor#NestedAccessor(Method, String)}. 030 * 031 * @param clazz 032 * type with the nested property. 033 * @param propertyName 034 * the first part of the nested property. 035 * @param nestedPropertyName 036 * the nested property, possibly containing more nesting levels. 037 * @see #NestedAccessor(Method, String) 038 */ 039 public NestedAccessor(final Class<?> clazz, final String propertyName, final String nestedPropertyName) 040 { 041 this(ClassUtils.getReadMethod(clazz, propertyName), nestedPropertyName); 042 } 043 044 /** 045 * Constructor. The getter will deliver the first level of the nesting. It's return value will be used to 046 * construct the next accessor for the nestedProperty. 047 * 048 * @param getter 049 * method delivering the first part of the nesting. 050 * @param nestedProperty 051 * property that should be available on the return object of the getter. Possibly nested as 052 * well. 053 * @see #NestedAccessor(Class, String, String) 054 */ 055 public NestedAccessor(final Method getter, final String nestedProperty) 056 { 057 this.nestedProperty = nestedProperty; 058 this.getter = getter; 059 } 060 061 /** 062 * Get the value from the source entity. If at any point the chaining results in a null value. The 063 * chaining should end and return <code>null</code>. 064 * 065 * @param fromEntity 066 * the entity on which the getter should operate. 067 * @return <code>null</code> if at any point in the chaining a property returned <code>null</code> or 068 * the value of the nested property. 069 */ 070 public Object getValue(Object fromEntity) throws IllegalAccessException, InvocationTargetException 071 { 072 Object propertyValue = getter.invoke(fromEntity); 073 return propertyValue == null ? null : getWrappedAccessor(propertyValue.getClass()).getValue( 074 propertyValue); 075 } 076 077 /** 078 * <p> 079 * Get the wrapped accessor, instantiate it lazily if needed. 080 * </p> 081 * 082 * <p> 083 * Normally the return type of the getter method delivers the correct type on which the nested property 084 * can be found. There is however a specific case in which this isn't true. It may be that a specific type 085 * is only known at runtime and that you need to access a property of that specific type. The specific 086 * type can be found at runtime when the wrapped accessor is constructed. But it does imply that all other 087 * access will result in the same type. 088 * </p> 089 * 090 * <p> 091 * A specific type implementation is found in PeriodicValueAdapter->BTWPercentage, here a property of 092 * BTWPercentage can be accessed through the adapter, but all other adapters should contain a 093 * BTWPercentage. 094 * </p> 095 * 096 * @param propertyType 097 * property type to use if getter doesn't yield the correct one. 098 * @return an {@link Accessor} for the wrapped property. 099 */ 100 private Accessor getWrappedAccessor(Class<?> propertyType) 101 { 102 if (wrappedAccessor == null) 103 { 104 try 105 { 106 wrappedAccessor = ClassUtils.getAccessorForProperty(getter.getReturnType(), nestedProperty); 107 } 108 catch (NoSuchMethodError nsme) 109 { 110 if (propertyType == null) 111 throw nsme; 112 wrappedAccessor = ClassUtils.getAccessorForProperty(propertyType, nestedProperty); 113 } 114 } 115 116 return wrappedAccessor; 117 } 118 119 /** 120 * {@inheritDoc} 121 */ 122 public Class<?> getPropertyType() 123 { 124 return getWrappedAccessor(null).getPropertyType(); 125 } 126 }