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 Writer} 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 NestedWriter implements Writer
013    {
014    
015        /** The nested writer to access and write the property. */
016        private final Writer nestedWriter;
017    
018        /** The getter to access the first level object. */
019        private final Method getter;
020    
021        /**
022         * Convenience constructor. Creates a getter method for the given class and property and reroutes to
023         * {@link NestedWriter#NestedWriter(Method, String)}.
024         * 
025         * @param clazz
026         *            type with the nested property.
027         * @param propertyName
028         *            the first part of the nested property.
029         * @param nestedPropertyName
030         *            the nested property, possibly containing more nesting levels. Only the last one in the line
031         *            needs the setter method.
032         * @see #NestedWriter(Method, String)
033         */
034        public NestedWriter(Class<?> clazz, String propertyName, String nestedPropertyName)
035        {
036            this(ClassUtils.getReadMethod(clazz, propertyName), nestedPropertyName);
037        }
038    
039        /**
040         * Constructor. The given getter should yield the return value on which the nested property exists.
041         * 
042         * @param getter
043         *            the method providing the entity.
044         * @param nestedPropertyName
045         *            the nested property on the entity.
046         */
047        public NestedWriter(Method getter, String nestedPropertyName)
048        {
049            this.getter = getter;
050            this.nestedWriter = ClassUtils.getWriterForProperty(getter.getReturnType(), nestedPropertyName);
051        }
052    
053        /**
054         * Set the value on the source entity. If at any point the chaining results in a null value. The chaining
055         * should end.
056         * 
057         * @param toEntity
058         *            the entity on which the getter should operate.
059         * @param newValue
060         *            the value to set.
061         */
062        public void setValue(Object toEntity, Object newValue) throws IllegalAccessException,
063                InvocationTargetException
064        {
065            Object propertyValue = getter.invoke(toEntity);
066            if (propertyValue != null)
067                nestedWriter.setValue(propertyValue, newValue);
068        }
069    
070        /**
071         * {@inheritDoc}
072         */
073        public Class<?> getPropertyType()
074        {
075            return nestedWriter.getPropertyType();
076        }
077    
078        /**
079         * Get the value from the source entity. If at any point the chaining results in a null value. The
080         * chaining should end and return <code>null</code>.
081         * 
082         * @param fromEntity
083         *            the entity on which the getter should operate.
084         * @return <code>null</code> if at any point in the chaining a property returned <code>null</code> or
085         *         the value of the nested property.
086         */
087        public Object getValue(Object fromEntity) throws IllegalAccessException, InvocationTargetException
088        {
089            Object propertyValue = getter.invoke(fromEntity);
090            return propertyValue == null ? null : nestedWriter.getValue(propertyValue);
091        }
092    }