/* * Copyright 2002-2010 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.ui.bind; import java.beans.PropertyDescriptor; import java.text.SimpleDateFormat; import java.util.Date; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jdal.beans.PropertyUtils; import org.jdal.beans.SpringConverter; import org.jdal.ui.DefaultModelHolder; import org.jdal.ui.ModelHolder; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeansException; import org.springframework.beans.PropertyAccessException; import org.springframework.beans.PropertyAccessorFactory; import org.springframework.beans.propertyeditors.CustomDateEditor; import org.springframework.core.convert.Property; import org.springframework.validation.BeanPropertyBindingResult; import org.springframework.validation.BindingResult; /** * Base class for Binders. Implement doBind() to do the binding. * Dereference ModelHolders when setting its as binded model. * * @author Jose Luis Martin * @since 1.1 */ public abstract class AbstractBinder extends SpringConverter implements PropertyBinder { private static final Log log = LogFactory.getLog(AbstractBinder.class); /** binded property name */ protected String propertyName; /** last value, used to revert o detect cicles */ protected Object oldValue; /** binded model */ private ModelHolder<Object> modelHolder; /** component object */ protected Object component; /** if true, binding is readOnly, ie from model to control */ protected boolean readOnly = false; private ControlBindingErrorProcessor errorProcessor = new ControlBindingErrorProcessor(); private BindingResult bindingResult; public AbstractBinder() { registerCustomEditor(Date.class, new CustomDateEditor(SimpleDateFormat.getDateTimeInstance(), true)); } /** * {@inheritDoc} */ public final void bind(Object component, String propertyName, Object model) { bind(component, propertyName, model, false); } /** * {@inheritDoc} */ public final void bind(Object component, String propertyName, Object model, boolean readOnly) { this.propertyName = propertyName; setModel(model); this.component = component; this.readOnly = readOnly; doBind(); } /** * Hook method to do bind via Component Listeners * By default, do nothing */ protected void doBind() { } public final void refresh() { doRefresh(); } public final void update() { if (!readOnly && getModel() != null) { bindingResult = createBindingResult(); doUpdate(); } } /** * Create a new Binding result, override to set nested paths on complex binders * @return a new instance of BindingResult for this model */ protected BindingResult createBindingResult() { Object model = getModel(); return new BeanPropertyBindingResult(model, model.getClass().getSimpleName()); } abstract protected void doRefresh(); abstract protected void doUpdate(); /** * Set value on binded object using the property name. * @param value the value to set */ protected void setValue(Object value) { BeanWrapper wrapper = getBeanWrapper(); Object convertedValue = convertIfNecessary(value, wrapper.getPropertyType(this.propertyName)); try { wrapper.setPropertyValue(propertyName, convertedValue); oldValue = value; } catch (PropertyAccessException pae) { log.error(pae); errorProcessor.processPropertyAccessException(component, pae, bindingResult); } } /** * Get value from model * @return model value */ protected Object getValue() { if (getModel() == null) return null; BeanWrapper wrapper = getBeanWrapper(); Object value = null; try { value = wrapper.getPropertyValue(propertyName); } catch (BeansException e) { log.error(e); } return value; } private BeanWrapper getBeanWrapper() { BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(getModel()); wrapper.setConversionService(getConversionService()); wrapper.registerCustomEditor(Date.class, new CustomDateEditor(SimpleDateFormat.getDateTimeInstance(), true)); return wrapper; } /** * @return Property for property binder */ protected Property getProperty() { PropertyDescriptor pd = getPropertyDescriptor(); return new Property(getModel().getClass(), pd.getReadMethod(), pd.getWriteMethod()); } // Getters and Setters public String getPropertyName() { return propertyName; } public void setPropertyName(String propertyName) { this.propertyName = propertyName; } protected Object getOldValue() { return oldValue; } protected void setOldValue(Object oldValue) { this.oldValue = oldValue; } public Object getModel() { return modelHolder != null ? modelHolder.getModel() : null; } @SuppressWarnings("unchecked") public void setModel(Object model) { if (model instanceof ModelHolder) { this.modelHolder = (ModelHolder<Object>) model; } else { model = new DefaultModelHolder<Object>(model); } } /** * @return the component */ public Object getComponent() { return component; } /** * @param component the component to set */ public void setComponent(Object component) { this.component = component; } public BindingResult getBindingResult() { return bindingResult; } public Class<?> getPropertyType() { return getPropertyDescriptor().getPropertyType(); } public PropertyDescriptor getPropertyDescriptor() { return PropertyUtils.getPropertyDescriptor(getModel().getClass(), propertyName); } }