All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.dragome.forms.bindings.client.bean.AbstractBeanModelProvider Maven / Gradle / Ivy

There is a newer version: 0.96-beta4
Show newest version
package com.dragome.forms.bindings.client.bean;

import com.dragome.forms.bindings.client.form.ListModelProvider;
import com.dragome.forms.bindings.client.form.ValueModelProvider;
import com.dragome.forms.bindings.client.value.AbstractMutableValueModel;
import com.dragome.forms.bindings.client.value.DelegatingValueModel;
import com.dragome.forms.bindings.client.value.ValueHolder;
import com.dragome.forms.bindings.client.value.ValueModel;
import com.dragome.model.interfaces.ValueChangeEvent;
import com.dragome.model.interfaces.ValueChangeHandler;

/**
 * Created by IntelliJ IDEA.
 * User: andrew
 * Date: Jul 1, 2010
 * Time: 8:45:59 AM
 * To change this template use File | Settings | File Templates.
 */
public abstract class AbstractBeanModelProvider extends AbstractMutableValueModel implements ValueModelProvider, ListModelProvider, HasDirtyModel
{
	protected DelegatingValueModel source= new DelegatingValueModel(new ValueHolder());
	private PropertyModelRegistry registry= new PropertyModelRegistry();
	private CollectionConverters collectionConverters= new CollectionConverters();
	private ValueHolder autoCommit= new ValueHolder(false);

	protected AbstractBeanModelProvider()
	{
		// Each BeanPropertyValue/ListModel monitors their source model and will automatically
		// readFromSource() when on value changes, so there's not explicit read from source to
		// call here.
		//
		// Our actual value is held in our delegating model so we always
		// fire a value change when it changes.
		source.addValueChangeHandler(new ValueChangeHandler()
		{
			public void onValueChange(ValueChangeEvent event)
			{
				fireValueChangeEvent(event.getValue());
			}
		});
	}

	public B getValue()
	{
		return source.getValue();
	}

	/**
	 * Sets the bean to use as the root for all models created by this provider.  All value models will update after this
	 * method has been called.
	 * 

* Please note if {@link #setBeanSource(com.pietschy.gwt.pectin.client.value.ValueModel)} has been called * with a model that isn't an instance of {@link com.pietschy.gwt.pectin.client.value.MutableValueModel} * then this method will fail. * * @param bean the bean */ public void setValue(B value) { source.setValue(value); } /** * @deprecated use {@link #setValue(B)} instead. */ @Deprecated public void setBean(B bean) { setValue(bean); } /** * @deprecated use {@link #getValue()} instead. */ @Deprecated public B getBean() { return getValue(); } /** * Sets the {@link com.pietschy.gwt.pectin.client.value.ValueModel} to be used as the source of this provider. All changes to the source * model will be tracked. *

* Please note that if the source is not an instance of {@link com.pietschy.gwt.pectin.client.value.MutableValueModel} * then any subsequent calls to {@link #setBean(Object)} will fail. * * @param beanSource the {@link com.pietschy.gwt.pectin.client.value.ValueModel} containing the source bean. */ public void setBeanSource(ValueModel beanSource) { source.setDelegate(beanSource); } public void setAutoCommit(boolean autoCommit) { this.autoCommit.setValue(autoCommit); } public boolean isAutoCommit() { return this.autoCommit.getValue(); } /** * Checkpoints the current state of all value models and clears the dirty state of all models. */ public void checkpoint() { registry.withEachModel(new PropertyModelVisitor() { public void visit(BeanPropertyModelBase model) { model.checkpoint(); } }); } /** * Resets all the models back to their last check pointed state. If checkpoint hasn't been called then * it will revert to the state when the source bean was last configured. */ public void revert() { registry.withEachModel(new PropertyModelVisitor() { public void visit(BeanPropertyModelBase model) { model.revert(); } }); } /** * Writes all outstanding changes to the underlying bean graph and clears all the dirty state. */ public void commit() { commit(true); } /** * Writes all outstanding changes to the underlying bean graph. If checkpoint * is true then the provider will be check pointed and the dirty state cleared. * If false then the changes are still written to the bean graph but the dirty state * remains as is. *

* This method is useful if you don't want to clear the dirty state until some time later, e.g. after * a RPC call has succeeded. * * @param checkpoint true to checkpoing the provider and clear the dirty state, false * to leave the dirty state as is. */ public void commit(final boolean checkpoint) { registry.withEachModel(new PropertyModelVisitor() { public void visit(BeanPropertyModelBase model) { if (model.isMutableProperty()) { model.writeToSource(checkpoint); } } }); } /** * * @deprecated use {@link #dirty()} instead. */ @Deprecated public ValueModel getDirtyModel() { return dirty(); } /** * Returns a value model that reflects this providers dirty state. * @return a value model that reflects this providers dirty state. */ public ValueModel dirty() { return registry.getDirtyModel(); } /** * Gets a {@link com.pietschy.gwt.pectin.client.list.ListModel} based on the specified property name and value type. Property types * of the generic interface types {@link java.util.Collection}, {@link java.util.List}, {@link java.util.Set}, {@link java.util.SortedSet} are supported * out of the box. Additional types can be supported by registering a suitable {@link com.pietschy.gwt.pectin.client.bean.CollectionConverter}. *

* Multiple calls to this method will return the same model. * * @param propertyPath the name of the property. * @param elementType the type contained by the collection * @return the {@link com.pietschy.gwt.pectin.client.list.ListModel} for the specified property. Multiple calls to this method will return the same * model. * @throws com.pietschy.gwt.pectin.client.bean.UnknownPropertyException if the bean doesn't define the specified property. * @throws com.pietschy.gwt.pectin.client.bean.UnsupportedCollectionTypeException * if a suitable {@link com.pietschy.gwt.pectin.client.bean.CollectionConverter} hasn't been registered * for the bean property collection type. * @see #registerCollectionConverter(Class, com.pietschy.gwt.pectin.client.bean.CollectionConverter) */ @SuppressWarnings("unchecked") public BeanPropertyListModel getListModel(String propertyPath, Class elementType) throws UnknownPropertyException, UnsupportedCollectionTypeException, IncorrectElementTypeException, NotCollectionPropertyException { BeanPropertyListModel listModel= registry.getListModel(propertyPath); if (listModel == null) { PropertyDescriptor descriptor= createPropertyDescriptor(propertyPath); if (!descriptor.isCollection()) { throw new NotCollectionPropertyException(descriptor); } Class realElementType= descriptor.getElementType(); if (!realElementType.equals(elementType)) { throw new IncorrectElementTypeException(descriptor, elementType); } Class collectionType= descriptor.getValueType(); CollectionConverter converter= collectionConverters.getConverter(collectionType); if (converter == null) { throw new UnsupportedCollectionTypeException(collectionType); } listModel= createListModel(getSourceModel(descriptor), descriptor, converter, autoCommit); registry.add(propertyPath, listModel); } else { // check that the already existing model was created with the same type. if (!listModel.getValueType().equals(elementType)) { throw new IllegalArgumentException("ListModel for path `" + propertyPath + "` already created with a different " + "element type: " + listModel.getValueType()); } } return (BeanPropertyListModel) listModel; } protected BeanPropertyListModel createListModel(ValueModel sourceModel, PropertyDescriptor descriptor, CollectionConverter converter, ValueModel autoCommit) { return new BeanPropertyListModel(sourceModel, descriptor, converter, autoCommit); } /** * Gets a {@link com.pietschy.gwt.pectin.client.value.ValueModel} for the specified bean property and the specified type. Multiple calls to this method * will return the same model. * * @param propertyPath the name of the property. * @param valueType the type of the property. * @return a {@link com.pietschy.gwt.pectin.client.value.ValueModel} for the specified bean property. Multiple calls to this method * will return the same model. * @throws com.pietschy.gwt.pectin.client.bean.UnknownPropertyException if the property isn't defined by the bean. * @throws com.pietschy.gwt.pectin.client.bean.IncorrectPropertyTypeException if the type of the property doesn't match the model type. */ @SuppressWarnings("unchecked") public BeanPropertyValueModel getValueModel(String propertyPath, Class valueType) throws UnknownPropertyException, IncorrectPropertyTypeException { BeanPropertyValueModel valueModel= registry.getValueModel(propertyPath); if (valueModel == null) { PropertyDescriptor descriptor= createPropertyDescriptor(propertyPath); if (!valueType.equals(descriptor.getValueType())) { throw new IncorrectPropertyTypeException(valueType, descriptor); } valueModel= createValueModel(getSourceModel(descriptor), descriptor, autoCommit); registry.add(propertyPath, valueModel); } else { // check that the already existing model was created with the same type. if (!valueModel.getValueType().equals(valueType)) { throw new IllegalArgumentException("ValueModel for path `" + propertyPath + "` already created with a different " + "type: " + valueModel.getValueType()); } } return (BeanPropertyValueModel) valueModel; } protected BeanPropertyValueModel createValueModel(ValueModel sourceModel, PropertyDescriptor descriptor, ValueModel autoCommit) { return new BeanPropertyValueModel(sourceModel, descriptor, autoCommit); } @SuppressWarnings("unchecked") ValueModel getSourceModel(PropertyDescriptor property) { if (property.isTopLevel()) { return this; } else { return getValueModel(property.getParentPath(), property.getBeanType()); } } /** * Called the first time a specific property path is requested. * * @param propertyPath the full path of the property * @return a {@link com.pietschy.gwt.pectin.client.bean.PropertyDescriptor} for the specified path. */ public abstract PropertyDescriptor createPropertyDescriptor(String propertyPath); /** * Registers a new converter for converting between collection based bean properties and the ListModel. * This allows uses to support collection types other than the generic interface types on their beans. * * @param collectionClass the collection type of the bean property. * @param converter the converter to use for bean properties of the specified collection type. */ public void registerCollectionConverter(Class collectionClass, CollectionConverter converter) { collectionConverters.register(collectionClass, converter); } }