org.metawidget.swing.widgetprocessor.binding.beanutils.BeanUtilsBindingProcessor.java Source code

Java tutorial

Introduction

Here is the source code for org.metawidget.swing.widgetprocessor.binding.beanutils.BeanUtilsBindingProcessor.java

Source

// Metawidget (licensed under LGPL)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package org.metawidget.swing.widgetprocessor.binding.beanutils;

import static org.metawidget.inspector.InspectionResultConstants.*;

import java.awt.Component;
import java.util.Map;
import java.util.Set;

import javax.swing.JComponent;
import javax.swing.JScrollPane;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.metawidget.inspector.impl.propertystyle.Property;
import org.metawidget.inspector.impl.propertystyle.PropertyStyle;
import org.metawidget.inspector.impl.propertystyle.ValueAndDeclaredType;
import org.metawidget.swing.SwingMetawidget;
import org.metawidget.swing.widgetprocessor.binding.BindingConverter;
import org.metawidget.util.ClassUtils;
import org.metawidget.util.CollectionUtils;
import org.metawidget.util.simple.PathUtils;
import org.metawidget.util.simple.PathUtils.TypeAndNames;
import org.metawidget.util.simple.StringUtils;
import org.metawidget.widgetprocessor.iface.AdvancedWidgetProcessor;
import org.metawidget.widgetprocessor.iface.WidgetProcessorException;

/**
 * Property binding implementation based on BeanUtils.
 * <p>
 * Note: <code>BeanUtils</code> does not bind <em>actions</em>, such as invoking a method when a
 * <code>JButton</code> is pressed. For that, see <code>ReflectionBindingProcessor</code> and
 * <code>MetawidgetActionStyle</code> or <code>SwingAppFrameworkActionStyle</code>.
 *
 * @author Richard Kennard, Stefan Ackermann
 */

public class BeanUtilsBindingProcessor
        implements AdvancedWidgetProcessor<JComponent, SwingMetawidget>, BindingConverter {

    //
    // Private members
    //

    private final PropertyStyle mPropertyStyle;

    //
    // Constructor
    //

    public BeanUtilsBindingProcessor() {

        this(new BeanUtilsBindingProcessorConfig());
    }

    public BeanUtilsBindingProcessor(BeanUtilsBindingProcessorConfig config) {

        mPropertyStyle = config.getPropertyStyle();
    }

    //
    // Public methods
    //

    public void onStartBuild(SwingMetawidget metawidget) {

        metawidget.putClientProperty(BeanUtilsBindingProcessor.class, null);
    }

    public JComponent processWidget(JComponent component, String elementName, Map<String, String> attributes,
            SwingMetawidget metawidget) {

        JComponent componentToBind = component;

        // Unwrap JScrollPanes (for JTextAreas etc)

        if (componentToBind instanceof JScrollPane) {
            componentToBind = (JComponent) ((JScrollPane) componentToBind).getViewport().getView();
        }

        // Nested Metawidgets are not bound, only remembered

        if (componentToBind instanceof SwingMetawidget) {

            State state = getState(metawidget);

            if (state.nestedMetawidgets == null) {
                state.nestedMetawidgets = CollectionUtils.newHashSet();
            }

            state.nestedMetawidgets.add((SwingMetawidget) component);
            return component;
        }

        // Determine value property

        String componentProperty = metawidget.getValueProperty(componentToBind);

        if (componentProperty == null) {
            return component;
        }

        String path = metawidget.getPath();

        if (PROPERTY.equals(elementName)) {
            path += StringUtils.SEPARATOR_FORWARD_SLASH_CHAR + attributes.get(NAME);
        }

        try {

            TypeAndNames typeAndNames = PathUtils.parsePath(path, StringUtils.SEPARATOR_FORWARD_SLASH_CHAR);
            Object sourceValue = mPropertyStyle.traverse(metawidget.getToInspect(), typeAndNames.getType(), false,
                    typeAndNames.getNamesAsArray()).getValue();

            // Convert 'com.Foo/bar/baz' into BeanUtils notation 'bar.baz'

            String names = typeAndNames.getNames().replace(StringUtils.SEPARATOR_FORWARD_SLASH_CHAR,
                    StringUtils.SEPARATOR_DOT_CHAR);
            SavedBinding binding = new SavedBinding(componentToBind, componentProperty, names,
                    TRUE.equals(attributes.get(NO_SETTER)));
            saveValueToWidget(binding, sourceValue);

            State state = getState(metawidget);

            if (state.bindings == null) {
                state.bindings = CollectionUtils.newHashSet();
            }

            state.bindings.add(binding);
        } catch (Exception e) {
            throw WidgetProcessorException.newException(e);
        }

        return component;
    }

    /**
     * Rebinds the Metawidget to the given Object.
     * <p>
     * This method is an optimization that allows clients to load a new object into the binding
     * <em>without</em> calling setToInspect, and therefore without reinspecting the object or
     * recreating the components. It is the client's responsbility to ensure the rebound object is
     * compatible with the original setToInspect.
     */

    public void rebind(Object toRebind, SwingMetawidget metawidget) {

        metawidget.updateToInspectWithoutInvalidate(toRebind);
        State state = getState(metawidget);

        // Our bindings

        if (state.bindings != null) {
            try {
                for (SavedBinding binding : state.bindings) {
                    String names = binding.getNames();
                    ValueAndDeclaredType valueAndDeclaredType = mPropertyStyle.traverse(toRebind,
                            toRebind.getClass().getName(), false,
                            names.split("\\" + StringUtils.SEPARATOR_DOT_CHAR));

                    if (valueAndDeclaredType.getDeclaredType() == null) {
                        throw WidgetProcessorException.newException("Property '" + names + "' has no getter");
                    }

                    saveValueToWidget(binding, valueAndDeclaredType.getValue());
                }
            } catch (Exception e) {
                throw WidgetProcessorException.newException(e);
            }
        }

        // Nested Metawidgets

        if (state.nestedMetawidgets != null) {
            for (SwingMetawidget nestedMetawidget : state.nestedMetawidgets) {
                rebind(toRebind, nestedMetawidget);
            }
        }
    }

    public void save(SwingMetawidget metawidget) {

        State state = getState(metawidget);

        // Our bindings

        if (state.bindings != null) {
            try {
                for (SavedBinding binding : state.bindings) {
                    if (!binding.isSettable()) {
                        continue;
                    }

                    Object componentValue = retrieveValueFromWidget(binding);
                    saveValueToObject(metawidget, binding.getNames(), componentValue);
                }
            } catch (Exception e) {
                throw WidgetProcessorException.newException(e);
            }
        }

        // Nested Metawidgets

        if (state.nestedMetawidgets != null) {
            for (SwingMetawidget nestedMetawidget : state.nestedMetawidgets) {
                save(nestedMetawidget);
            }
        }
    }

    public Object convertFromString(String value, Class<?> expectedType) {

        return ConvertUtils.convert(value, expectedType);
    }

    public void onEndBuild(SwingMetawidget metawidget) {

        // Do nothing
    }

    //
    // Protected methods
    //

    /**
     * Save the given value into the given source at the location specified by the given names.
     * <p>
     * Clients may override this method to incorporate their own setter convention.
     *
     * @param componentValue
     *            the raw value from the <code>JComponent</code>
     */

    protected void saveValueToObject(SwingMetawidget metawidget, String names, Object componentValue)
            throws Exception {

        Object source = metawidget.getToInspect();

        // Traverse to the setter...
        //
        // Note: do not use BeanUtils.setProperty( source, names, componentValue ), as this can only
        // handle JavaBean properties

        String[] namesAsArray = names.split("\\" + StringUtils.SEPARATOR_DOT_CHAR);
        Object parent = mPropertyStyle.traverse(source, source.getClass().getName(), true, namesAsArray).getValue();

        if (parent == null) {
            return;
        }

        // ...determine its type...

        Class<?> parentClass = parent.getClass();
        String lastName = namesAsArray[namesAsArray.length - 1];

        Property property = mPropertyStyle.getProperties(parentClass.getName()).get(lastName);

        // ...convert if necessary (BeanUtils.setProperty usually does this for us)...
        //
        // Note: if this line fails, to build, check commons-beanutils comes first on the
        // CLASSPATH

        Object convertedValue = ConvertUtils.convert(componentValue, ClassUtils.niceForName(property.getType()));

        // ...and set it

        property.write(parent, convertedValue);
    }

    protected Object retrieveValueFromWidget(SavedBinding binding) throws Exception {

        return PropertyUtils.getProperty(binding.getComponent(), binding.getComponentProperty());
    }

    protected void saveValueToWidget(SavedBinding binding, Object sourceValue) throws Exception {

        BeanUtils.setProperty(binding.getComponent(), binding.getComponentProperty(), sourceValue);
    }

    //
    // Private methods
    //

    private State getState(SwingMetawidget metawidget) {

        State state = (State) metawidget.getClientProperty(BeanUtilsBindingProcessor.class);

        if (state == null) {
            state = new State();
            metawidget.putClientProperty(BeanUtilsBindingProcessor.class, state);
        }

        return state;
    }

    //
    // Inner class
    //

    /**
     * Simple, lightweight structure for saving state.
     */

    /* package private */static class State {

        /* package private */Set<SavedBinding> bindings;

        /* package private */Set<SwingMetawidget> nestedMetawidgets;
    }

    static class SavedBinding {

        //
        //
        // Private members
        //
        //

        private Component mComponent;

        private String mComponentProperty;

        private String mNames;

        private boolean mNoSetter;

        //
        //
        // Constructor
        //
        //

        public SavedBinding(Component component, String componentProperty, String names, boolean noSetter) {

            mComponent = component;
            mComponentProperty = componentProperty;
            mNames = names;
            mNoSetter = noSetter;
        }

        //
        //
        // Public methods
        //
        //

        public Component getComponent() {

            return mComponent;
        }

        public String getComponentProperty() {

            return mComponentProperty;
        }

        /**
         * Property names into the source object.
         * <p>
         * Stored in BeanUtils style <code>foo.bar.baz</code>.
         */

        public String getNames() {

            return mNames;
        }

        public boolean isSettable() {

            return !mNoSetter;
        }
    }
}