org.jdal.swing.bind.AutoBinder.java Source code

Java tutorial

Introduction

Here is the source code for org.jdal.swing.bind.AutoBinder.java

Source

/*
 * Copyright 2009-2011 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jdal.swing.bind;

import java.beans.PropertyDescriptor;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jdal.swing.ModelHolder;
import org.jdal.ui.Binder;
import org.jdal.ui.bind.ConfigurableControlAccessorFactory;
import org.jdal.ui.bind.ControlAccessor;
import org.jdal.ui.bind.ControlAccessorFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.ConfigurablePropertyAccessor;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.PropertyAccessException;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.BindingErrorProcessor;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DefaultBindingErrorProcessor;

/**
 * Do an automatic binding of a view using reflection. Bind
 * controls with name equals to property names in model.
 * 
 * @author Jose Luis Martin
 * @see org.jdal.ui.Binder
 * @since 1.1
 */
public class AutoBinder<T> implements Binder<T> {

    /** Log */
    private static final Log log = LogFactory.getLog(AutoBinder.class);
    /** View to bind on */
    private Object view;
    /** Control accessor factory to use for create control accessors */
    private ControlAccessorFactory controlAccessorFactory;
    /** PropertyAccessor for access view fields */
    private ConfigurablePropertyAccessor viewPropertyAccessor;
    /** PropertyAccessor for access model properties */
    private BeanWrapper modelPropertyAccessor;
    /** Set with property names to ingnore on binding commands */
    private Set<String> ignoredProperties = new HashSet<String>();
    /** Binded model */
    private T model;
    /** Command to execute on refresh */
    private RefreshCommand refreshCommand = new RefreshCommand();
    /** Command to execute on update */
    private UpdateCommand updateCommand = new UpdateCommand();
    /** Process binding errors */
    private BindingErrorProcessor errorProcessor = new DefaultBindingErrorProcessor();
    /** binding errors */
    private BindingResult bindingResult;
    /** Hold property name -> ControlAccessor mapping */
    private Map<String, ControlAccessor> controlAccessorMap = new HashMap<String, ControlAccessor>();

    /**
     * Create an AutoBinder for a View
     * @param view View to bind.
     */
    public AutoBinder(ModelHolder<T> view) {
        this(view, view.getModel());
    }

    /**
     * Create a Binder for view and model
     * @param view
     * @param model
     */
    public AutoBinder(Object view, T model) {
        this.view = view;
        this.model = model;
        viewPropertyAccessor = new DirectFieldAccessor(this.view);
        bindingResult = new BeanPropertyBindingResult(model, "model");
    }

    public void bind(String viewField, String propertyName) throws UndefinedAccessorException {
        Object control = viewPropertyAccessor.getPropertyValue(propertyName);
        if (control != null) {
            ControlAccessor accessor = controlAccessorFactory.getControlAccessor(control);
            if (accessor != null) {
                controlAccessorMap.put(propertyName, accessor);
            } else {
                String msg = "Not found ControlAcessor for control class [" + control.getClass().getName() + "]";
                throw new UndefinedAccessorException(msg);
            }
        }

    }

    /**
     * {@inheritDoc}
     */
    public void refresh() {
        executeBinderCommand(refreshCommand);
    }

    /**
     * {@inheritDoc}
     */
    public void update() {
        executeBinderCommand(updateCommand);
    }

    /**
     * Execute BinderCommand (update or refresh) for all model properties
     * @param command Command to execute.
     */
    private void executeBinderCommand(BinderCommand command) {
        modelPropertyAccessor = PropertyAccessorFactory.forBeanPropertyAccess(model);
        // iterate on model properties
        for (PropertyDescriptor pd : modelPropertyAccessor.getPropertyDescriptors()) {
            String propertyName = pd.getName();
            if (!ignoredProperties.contains(propertyName)) {
                ControlAccessor controlAccessor = getControlAccessor(propertyName);
                if (controlAccessor != null)
                    command.execute(controlAccessor, propertyName);
            }
        }
    }

    /**
     * Gets control accessor
     * @param control to get accessor
     * @return accessor or null if none found.
     */
    private ControlAccessor getControlAccessor(String name) {
        // try map first
        if (controlAccessorMap.containsKey(name))
            return controlAccessorMap.get(name);

        // try matching view property
        ControlAccessor accessor = null;
        if (viewPropertyAccessor.isReadableProperty(name)) {
            Object control = viewPropertyAccessor.getPropertyValue(name);
            if (control != null) {
                if (log.isDebugEnabled())
                    log.debug("Found control: " + control.getClass().getSimpleName() + " for property: " + name);
                accessor = getControlAccessorFactory().getControlAccessor(control);
            }

        }
        return accessor;
    }

    /**
     * {@inheritDoc}
     */
    public T getModel() {
        return model;
    }

    /**
     * {@inheritDoc}
     */
    public void setModel(T model) {
        this.model = model;
    }

    /**
     * @return the controlAccessorFactory
     */
    public ControlAccessorFactory getControlAccessorFactory() {
        if (controlAccessorFactory == null)
            controlAccessorFactory = new ConfigurableControlAccessorFactory();

        return controlAccessorFactory;
    }

    /**
     * @param controlAccessorFactory the controlAccessorFactory to set
     */
    public void setControlAccessorFactory(ControlAccessorFactory controlAccessorFactory) {
        this.controlAccessorFactory = controlAccessorFactory;
    }

    /**
     * Add a property name  to ignore on binding.
     * @param propertyName property name to ignore
     */
    public void ignoreProperty(String propertyName) {
        ignoredProperties.add(propertyName);
    }

    /**
     * @return the ignoredProperties
     */
    public Set<String> getIgnoredProperties() {
        return ignoredProperties;
    }

    /**
     * @param ignoredProperties the ignoredProperties to set
     */
    public void setIgnoredProperties(Set<String> ignoredProperties) {
        this.ignoredProperties = ignoredProperties;
    }

    /**
     * Add a Collection of property names to ignore on binding
     * @param c Collection of property names.
     */
    public void ignoreProperties(Collection<? extends String> c) {
        ignoredProperties.addAll(c);
    }

    /**
     * Return the Binding result
     * @return the binding result
     */
    public BindingResult getBindingResult() {
        return bindingResult;
    }

    /**
     *  Binder Command Callback
     * @author Jose Luis Martin - (jlm@joseluismartin.info)
     */
    interface BinderCommand {
        void execute(ControlAccessor accessor, String propertyName);
    }

    /**
     * Update Command
     */
    class UpdateCommand implements BinderCommand {

        public void execute(ControlAccessor controlAccessor, String name) {
            try {
                modelPropertyAccessor.setPropertyValue(name, controlAccessor.getControlValue());
            } catch (PropertyAccessException pae) {
                errorProcessor.processPropertyAccessException(pae, bindingResult);
            }
        }
    }

    /**
     * Refresh Command
     */
    class RefreshCommand implements BinderCommand {

        public void execute(ControlAccessor controlAccessor, String name) {
            controlAccessor.setControlValue(modelPropertyAccessor.getPropertyValue(name));
        }
    }

}