package org.jdal.ui.bind; import java.beans.PropertyDescriptor; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jdal.beans.PropertyUtils; import org.jdal.ui.Binder; import org.jdal.ui.ModelHolder; import org.springframework.beans.BeanWrapper; import org.springframework.beans.PropertyAccessor; import org.springframework.beans.PropertyAccessorFactory; import org.springframework.validation.BeanPropertyBindingResult; import org.springframework.validation.BindingResult; /** * Composite Binder groups property binders. * * @author Jose Luis Martin - (jlm@joseluismartin.info) * @param <T> default model to use when binding properties */ @SuppressWarnings("unchecked") public class CompositeBinder<T> implements Binder<T>, BinderHolder, ModelHolder<T>, Serializable { private static final Log log = LogFactory.getLog(CompositeBinder.class); private BinderFactory binderFactory; private Map<String, Binder<T>> binders = new HashMap<String, Binder<T>>(); /** Default model to bind on for property binders */ private T model; /** Binding result */ private BindingResult bindingResult; private List<String> ignoredProperties = new ArrayList<String>(); /** * Create new CompositeBinder */ public CompositeBinder() { } /** * @param model model to bind on */ public CompositeBinder(T model) { this.model = model; } public void bind(Object component, String propertyName) { bind(component, propertyName, false); } public void bind(Object component, String propertyName, boolean readOnly) { bind(component, propertyName, this, readOnly); } public void bind(Object component, String propertyName, Object model, boolean readOnly) { PropertyBinder binder = binderFactory.getBinder(component.getClass()); if (binder != null) { binder.bind(component, propertyName, model, readOnly); addBinder(propertyName, binder); } } public void addBinder(String propertyName, PropertyBinder binder) { binders.put(propertyName, (Binder<T>) binder); } public void refresh() { for (Binder<?> b : binders.values()) b.refresh(); } public void addBinder(Binder<?> binder, String name) { binders.put(name, (Binder<T>) binder); } public void update() { bindingResult = null; for (Binder<?> b : binders.values()) b.update(); } public PropertyBinder getBinder(String propertyName) { PropertyBinder binder = (PropertyBinder) binders.get(propertyName); if (binder != null) return binder; if (PropertyUtils.isNested(propertyName)) { BinderHolder binderHolder = (BinderHolder) binders.get(PropertyUtils.getFirstPropertyName(propertyName)); return binderHolder != null ? binderHolder.getBinder(PropertyUtils.getNestedPath(propertyName)) : null; } return null; } public Set<String> getPropertyNames() { return binders.keySet(); } public Collection<Binder<T>> getPropertyBinders() { return binders.values(); } /** * @return the binderFactory */ public BinderFactory getBinderFactory() { return binderFactory; } /** * @param binderFactory the binderFactory to set */ public void setBinderFactory(BinderFactory binderFactory) { this.binderFactory = binderFactory; } public T getModel() { return model; } public void setModel(T model) { this.model = model; } /** * {@inheritDoc} */ public BindingResult getBindingResult() { if (getModel() == null) return null; if (bindingResult == null) { createBindingResult(); for (Binder<?> b : binders.values()) { if (b.getBindingResult() != null && bindingResult.getObjectName().equals(b.getBindingResult().getObjectName())) bindingResult.addAllErrors(b.getBindingResult()); } } return bindingResult; } private void createBindingResult() { bindingResult = new BeanPropertyBindingResult(getModel(), getModel().getClass().getSimpleName()); } public void autobind(Object view) { BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(getModel()); PropertyAccessor viewPropertyAccessor = new DirectFieldAccessor(view); // iterate on model properties for (PropertyDescriptor pd : bw.getPropertyDescriptors()) { String propertyName = pd.getName(); if ( !ignoredProperties.contains(propertyName) && viewPropertyAccessor.isReadableProperty(propertyName)) { Object control = viewPropertyAccessor.getPropertyValue(propertyName); if (control != null) { if (log.isDebugEnabled()) log.debug("Found control: " + control.getClass().getSimpleName() + " for property: " + propertyName); bind(control, propertyName); } } } } /** * @return the ignoredProperties */ public List<String> getIgnoredProperties() { return ignoredProperties; } /** * @param ignoredProperties the ignoredProperties to set */ public void setIgnoredProperties(List<String> ignoredProperties) { this.ignoredProperties = ignoredProperties; } /** * Add a property name to ignore on binding. * @param propertyName property name to ignore */ public void ignoreProperty(String propertyName) { ignoredProperties.add(propertyName); } }