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    }