summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/com/vaadin/Application.java47
-rw-r--r--src/com/vaadin/ui/AbstractComponent.java2
-rw-r--r--src/com/vaadin/ui/AbstractField.java315
-rw-r--r--src/com/vaadin/ui/AbstractTextField.java2
-rw-r--r--src/com/vaadin/ui/Button.java2
-rw-r--r--src/com/vaadin/ui/DateField.java20
6 files changed, 271 insertions, 117 deletions
diff --git a/src/com/vaadin/Application.java b/src/com/vaadin/Application.java
index f751431e63..c96a0adc6a 100644
--- a/src/com/vaadin/Application.java
+++ b/src/com/vaadin/Application.java
@@ -20,6 +20,9 @@ import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
+import com.vaadin.data.util.converter.Converter;
+import com.vaadin.data.util.converter.ConverterFactory;
+import com.vaadin.data.util.converter.DefaultConverterFactory;
import com.vaadin.service.ApplicationContext;
import com.vaadin.terminal.ApplicationResource;
import com.vaadin.terminal.DownloadStream;
@@ -33,6 +36,7 @@ import com.vaadin.terminal.gwt.server.ChangeVariablesErrorEvent;
import com.vaadin.terminal.gwt.server.PortletApplicationContext;
import com.vaadin.terminal.gwt.server.WebApplicationContext;
import com.vaadin.ui.AbstractComponent;
+import com.vaadin.ui.AbstractField;
import com.vaadin.ui.Window;
/**
@@ -189,6 +193,13 @@ public abstract class Application implements URIHandler,
private Terminal.ErrorListener errorHandler = this;
/**
+ * The converter factory that is used for all fields in the application.
+ */
+ // private ConverterFactory converterFactory = new
+ // DefaultConverterFactory();
+ private static ConverterFactory converterFactory = new DefaultConverterFactory();
+
+ /**
* <p>
* Gets a window by name. Returns <code>null</code> if the application is
* not running or it does not contain a window corresponding to the name.
@@ -1280,6 +1291,42 @@ public abstract class Application implements URIHandler,
}
/**
+ * Gets the {@link ConverterFactory} used to locate a suitable
+ * {@link Converter} for fields in the application.
+ *
+ * See {@link #setConverterFactory(ConverterFactory)} for more details
+ *
+ * @return The converter factory used in the application
+ */
+ // public ConverterFactory getConverterFactory() {
+ // return converterFactory;
+ // }
+ // FIXME: Should not be static
+ public static ConverterFactory getConverterFactory() {
+ return converterFactory;
+ }
+
+ /**
+ * Sets the {@link ConverterFactory} used to locate a suitable
+ * {@link Converter} for fields in the application.
+ *
+ * The {@link ConverterFactory} is used to find a suitable converter when
+ * binding data to a UI component and the data type does not match the UI
+ * component type, e.g. binding a Double to a TextField (which is based on a
+ * String).
+ *
+ * The {@link Converter} for an individual field can be overridden using
+ * {@link AbstractField#setValueConverter(Converter)}.
+ *
+ * @param converterFactory
+ * The converter factory used in the application
+ */
+ // FIXME: Should not be static
+ public static void setConverterFactory(ConverterFactory converterFactory) {
+ Application.converterFactory = converterFactory;
+ }
+
+ /**
* Contains the system messages used to notify the user about various
* critical situations that can occur.
* <p>
diff --git a/src/com/vaadin/ui/AbstractComponent.java b/src/com/vaadin/ui/AbstractComponent.java
index b9fffac35f..79300cbc9c 100644
--- a/src/com/vaadin/ui/AbstractComponent.java
+++ b/src/com/vaadin/ui/AbstractComponent.java
@@ -327,6 +327,8 @@ public abstract class AbstractComponent implements Component, MethodEventSource
*/
public void setLocale(Locale locale) {
this.locale = locale;
+
+ // FIXME: Reload value if there is a converter
requestRepaint();
}
diff --git a/src/com/vaadin/ui/AbstractField.java b/src/com/vaadin/ui/AbstractField.java
index 71fa32f245..dbd6094300 100644
--- a/src/com/vaadin/ui/AbstractField.java
+++ b/src/com/vaadin/ui/AbstractField.java
@@ -12,11 +12,14 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
+import com.vaadin.Application;
import com.vaadin.data.Buffered;
import com.vaadin.data.Property;
import com.vaadin.data.Validatable;
import com.vaadin.data.Validator;
import com.vaadin.data.Validator.InvalidValueException;
+import com.vaadin.data.util.converter.Converter;
+import com.vaadin.data.util.converter.ConverterFactory;
import com.vaadin.event.Action;
import com.vaadin.event.ShortcutAction;
import com.vaadin.event.ShortcutListener;
@@ -64,6 +67,11 @@ public abstract class AbstractField<T> extends AbstractComponent implements
private T value;
/**
+ * A converter used to convert from the data model type to the field type
+ * and vice versa.
+ */
+ private Converter<Object, T> valueConverter = null;
+ /**
* Connected data-source.
*/
private Property<?> dataSource = null;
@@ -178,9 +186,14 @@ public abstract class AbstractField<T> extends AbstractComponent implements
&& getErrorMessage() != null;
}
- /*
- * Gets the field type Don't add a JavaDoc comment here, we use the default
- * documentation from the implemented interface.
+ /**
+ * Returns the type of the Field. The methods <code>getValue</code> and
+ * <code>setValue</code> must be compatible with this type: one must be able
+ * to safely cast the value returned from <code>getValue</code> to the given
+ * type and pass any variable assignable to this type as an argument to
+ * <code>setValue</code>.
+ *
+ * @return the type of the Field
*/
public abstract Class<? extends T> getType();
@@ -230,14 +243,14 @@ public abstract class AbstractField<T> extends AbstractComponent implements
public void commit() throws Buffered.SourceException, InvalidValueException {
if (dataSource != null && !dataSource.isReadOnly()) {
if ((isInvalidCommitted() || isValid())) {
- final T newValue = getValue();
+ final T fieldValue = getFieldValue();
try {
// Commits the value to datasource.
valueWasModifiedByDataSourceDuringCommit = false;
committingValueToDataSource = true;
- // TODO cast required until conversions applied
- ((Property) dataSource).setValue(newValue);
+ getPropertyDataSource().setValue(
+ convertToDataSource(fieldValue));
} catch (final Throwable e) {
@@ -260,8 +273,8 @@ public abstract class AbstractField<T> extends AbstractComponent implements
boolean repaintNeeded = false;
// The abstract field is not modified anymore
- if (modified) {
- modified = false;
+ if (isModified()) {
+ setModified(false);
repaintNeeded = true;
}
@@ -288,14 +301,11 @@ public abstract class AbstractField<T> extends AbstractComponent implements
if (dataSource != null) {
// Gets the correct value from datasource
- Object newValue;
+ T newFieldValue;
try {
// Discards buffer by overwriting from datasource
- newValue = dataSource.getValue();
- if (String.class == getType() && newValue != null) {
- newValue = newValue.toString();
- }
+ newFieldValue = convertFromDataSource(getDataSourceValue());
// If successful, remove set the buffering state to be ok
if (currentBufferedSourceException != null) {
@@ -303,6 +313,7 @@ public abstract class AbstractField<T> extends AbstractComponent implements
requestRepaint();
}
} catch (final Throwable e) {
+ // FIXME: What should really be done here if conversion fails?
// Sets the buffering state
currentBufferedSourceException = new Buffered.SourceException(
@@ -314,23 +325,43 @@ public abstract class AbstractField<T> extends AbstractComponent implements
}
final boolean wasModified = isModified();
- modified = false;
+ setModified(false);
// If the new value differs from the previous one
- if ((newValue == null && value != null)
- || (newValue != null && !newValue.equals(value))) {
- // TODO use converter
- setInternalValue(newValue);
+ if (!equals(newFieldValue, value)) {
+ setInternalValue(newFieldValue);
fireValueChange(false);
- }
-
- // If the value did not change, but the modification status did
- else if (wasModified) {
+ } else if (wasModified) {
+ // If the value did not change, but the modification status did
requestRepaint();
}
}
}
+ private Object getDataSourceValue() {
+ return dataSource.getValue();
+ }
+
+ /**
+ * Returns the value that is or should be displayed in the field. This is
+ * always of type T.
+ *
+ * This method should return the same as
+ * convertFromDataSource(getDataSourceValue()) if there are no buffered
+ * changes in the field.
+ *
+ * @return The value of the field
+ */
+ private T getFieldValue() {
+ // Give the value from abstract buffers if the field if possible
+ if (dataSource == null || !isReadThrough() || isModified()) {
+ return value;
+ }
+
+ // There is no buffered value so use whatever the data model provides
+ return convertFromDataSource(getDataSourceValue());
+ }
+
/*
* Has the field been modified since the last commit()? Don't add a JavaDoc
* comment here, we use the default documentation from the implemented
@@ -340,6 +371,10 @@ public abstract class AbstractField<T> extends AbstractComponent implements
return modified;
}
+ private void setModified(boolean modified) {
+ this.modified = modified;
+ }
+
/*
* Tests if the field is in write-through mode. Don't add a JavaDoc comment
* here, we use the default documentation from the implemented interface.
@@ -383,13 +418,8 @@ public abstract class AbstractField<T> extends AbstractComponent implements
return;
}
readThroughMode = readThrough;
- if (!isModified() && readThroughMode && dataSource != null) {
- Object newValue = dataSource.getValue();
- if (String.class == getType() && newValue != null) {
- newValue = newValue.toString();
- }
- // TODO use converter
- setInternalValue(newValue);
+ if (!isModified() && readThroughMode && getPropertyDataSource() != null) {
+ setInternalValue(convertFromDataSource(getDataSourceValue()));
fireValueChange(false);
}
}
@@ -423,7 +453,7 @@ public abstract class AbstractField<T> extends AbstractComponent implements
* @since 7.0
*/
public String getStringValue() {
- final Object value = getValue();
+ final Object value = getFieldValue();
if (value == null) {
return null;
}
@@ -441,61 +471,57 @@ public abstract class AbstractField<T> extends AbstractComponent implements
* <p>
* Note that the object returned is compatible with getType(). For example,
* if the type is String, this returns Strings even when the underlying
- * datasource is of some other type. In order to access the native type of
- * the datasource, use getPropertyDatasource().getValue() instead.
+ * datasource is of some other type. In order to access the native value of
+ * the datasource, use getDataSourceValue() instead.
* </p>
*
* <p>
* Since Vaadin 7.0, no implicit conversions between other data types and
- * String are performed, but the converter is used if set.
+ * String are performed, but a value converter is used if set.
* </p>
*
* @return the current value of the field.
* @throws Property.ConversionException
*/
public T getValue() {
-
- // Give the value from abstract buffers if the field if possible
- if (dataSource == null || !isReadThrough() || isModified()) {
- return value;
- }
-
- Object result = dataSource.getValue();
- // TODO perform correct conversion, no cast or toString()
- if (String.class == getType() && result != null) {
- result = result.toString();
- }
- return (T) result;
+ return getFieldValue();
}
/**
* Sets the value of the field.
*
- * @param newValue
+ * @param newFieldValue
* the New value of the field.
* @throws Property.ReadOnlyException
* @throws Property.ConversionException
*/
- public void setValue(Object newValue) throws Property.ReadOnlyException,
- Property.ConversionException {
- setValue(newValue, false);
+ public void setValue(Object newFieldValue)
+ throws Property.ReadOnlyException, Property.ConversionException {
+ // This check is needed as long as setValue accepts Object instead of T
+ if (newFieldValue != null) {
+ if (!getType().isAssignableFrom(newFieldValue.getClass())) {
+ throw new ConversionException("Value of type "
+ + newFieldValue.getClass() + " cannot be assigned to "
+ + getClass().getName());
+ }
+ }
+ setValue((T) newFieldValue, false);
}
/**
* Sets the value of the field.
*
- * @param newValue
+ * @param newFieldValue
* the New value of the field.
* @param repaintIsNotNeeded
* True iff caller is sure that repaint is not needed.
* @throws Property.ReadOnlyException
* @throws Property.ConversionException
*/
- protected void setValue(Object newValue, boolean repaintIsNotNeeded)
+ protected void setValue(T newFieldValue, boolean repaintIsNotNeeded)
throws Property.ReadOnlyException, Property.ConversionException {
- if ((newValue == null && value != null)
- || (newValue != null && !newValue.equals(value))) {
+ if (!equals(newFieldValue, value)) {
// Read only fields can not be changed
if (isReadOnly()) {
@@ -514,14 +540,14 @@ public abstract class AbstractField<T> extends AbstractComponent implements
if (v != null) {
for (final Iterator<Validator> i = v.iterator(); i
.hasNext();) {
- (i.next()).validate(newValue);
+ (i.next()).validate(newFieldValue);
}
}
}
// Changes the value
- setInternalValue(newValue);
- modified = dataSource != null;
+ setInternalValue(newFieldValue);
+ setModified(dataSource != null);
valueWasModifiedByDataSourceDuringCommit = false;
// In write through mode , try to commit
@@ -531,10 +557,11 @@ public abstract class AbstractField<T> extends AbstractComponent implements
// Commits the value to datasource
committingValueToDataSource = true;
- ((Property) dataSource).setValue(newValue);
+ getPropertyDataSource().setValue(
+ convertToDataSource(newFieldValue));
// The buffer is now unmodified
- modified = false;
+ setModified(false);
} catch (final Throwable e) {
@@ -570,6 +597,13 @@ public abstract class AbstractField<T> extends AbstractComponent implements
}
}
+ private static boolean equals(Object value1, Object value2) {
+ if (value1 == null) {
+ return value2 == null;
+ }
+ return value1.equals(value2);
+ }
+
/* External data source */
/**
@@ -630,21 +664,23 @@ public abstract class AbstractField<T> extends AbstractComponent implements
// Sets the new data source
dataSource = newDataSource;
+ // Check if the current converter is compatible. If not, get a new one
+ if (newDataSource == null) {
+ setValueConverter(null);
+ } else if (!isValueConverterType(newDataSource.getType())) {
+ setValueConverterFromFactory(newDataSource.getType());
+ }
// Gets the value from source
try {
if (dataSource != null) {
- Object newValue = dataSource.getValue();
- if (String.class == getType() && newValue != null) {
- newValue = newValue.toString();
- }
- // TODO use converter
- setInternalValue(newValue);
+ T fieldValue = convertFromDataSource(getDataSourceValue());
+ setInternalValue(fieldValue);
}
- modified = false;
+ setModified(false);
} catch (final Throwable e) {
currentBufferedSourceException = new Buffered.SourceException(this,
e);
- modified = true;
+ setModified(true);
}
// Listens the new data source if possible
@@ -675,6 +711,87 @@ public abstract class AbstractField<T> extends AbstractComponent implements
}
}
+ private void setValueConverterFromFactory(Class<?> datamodelType) {
+ // FIXME Use thread local to get application
+ ConverterFactory factory = Application.getConverterFactory();
+
+ Converter<?, T> converter = (Converter<?, T>) factory.createConverter(
+ datamodelType, getType());
+
+ setValueConverter(converter);
+ }
+
+ private boolean isValueConverterType(Class<?> type) {
+ if (getValueConverter() == null) {
+ return false;
+ }
+ return getValueConverter().getSourceType().isAssignableFrom(type);
+ }
+
+ @SuppressWarnings("unchecked")
+ private T convertFromDataSource(Object newValue) {
+ if (valueConverter != null) {
+ return valueConverter.convertFromSourceToTarget(newValue,
+ getLocale());
+ }
+ if (newValue == null) {
+ return null;
+ }
+
+ if (getType().isAssignableFrom(newValue.getClass())) {
+ return (T) newValue;
+ } else {
+ throw new ConversionException(
+ "Unable to convert value of type "
+ + newValue.getClass().getName()
+ + " to "
+ + getType()
+ + ". No value converter is set and the types are not compatible.");
+ }
+ }
+
+ private Object convertToDataSource(T fieldValue)
+ throws Converter.ConversionException {
+ if (valueConverter != null) {
+ /*
+ * If there is a value converter, always use it. It must convert or
+ * throw an exception.
+ */
+ return valueConverter.convertFromTargetToSource(fieldValue,
+ getLocale());
+ }
+
+ if (fieldValue == null) {
+ // Null should always be passed through the converter but if there
+ // is no converter we can safely return null
+ return null;
+ }
+ if (getPropertyDataSource() != null) {
+ /*
+ * Data source is set
+ */
+ if (getPropertyDataSource().getType().isAssignableFrom(
+ fieldValue.getClass())) {
+ return fieldValue;
+ } else {
+ throw new Converter.ConversionException(
+ "Unable to convert value of type "
+ + fieldValue.getClass().getName()
+ + " to "
+ + getPropertyDataSource().getType()
+ + ". No value converter is set and the types are not compatible.");
+
+ }
+ }
+
+ throw new Converter.ConversionException(
+ "Unable to convert value of type "
+ + fieldValue.getClass().getName()
+ + " to "
+ + getType()
+ + ". No value converter is set and the types are not compatible.");
+ }
+
/* Validation */
/**
@@ -769,12 +886,12 @@ public abstract class AbstractField<T> extends AbstractComponent implements
// Initialize temps
Validator.InvalidValueException firstError = null;
LinkedList<InvalidValueException> errors = null;
- final Object value = getValue();
+ final Object fieldValue = getFieldValue();
// Gets all the validation errors
for (final Iterator<Validator> i = validators.iterator(); i.hasNext();) {
try {
- (i.next()).validate(value);
+ (i.next()).validate(fieldValue);
} catch (final Validator.InvalidValueException e) {
if (firstError == null) {
firstError = e;
@@ -1028,10 +1145,8 @@ public abstract class AbstractField<T> extends AbstractComponent implements
public void valueChange(Property.ValueChangeEvent event) {
if (isReadThrough()) {
if (committingValueToDataSource) {
- boolean propertyNotifiesOfTheBufferedValue = event
- .getProperty().getValue() == value
- || (value != null && value.equals(event.getProperty()
- .getValue()));
+ boolean propertyNotifiesOfTheBufferedValue = equals(event
+ .getProperty().getValue(), value);
if (!propertyNotifiesOfTheBufferedValue) {
/*
* Property (or chained property like PropertyFormatter) now
@@ -1054,8 +1169,7 @@ public abstract class AbstractField<T> extends AbstractComponent implements
}
private void readValueFromProperty(Property.ValueChangeEvent event) {
- // TODO use converter or check type otherwise
- setInternalValue(event.getProperty().getValue());
+ setInternalValue(convertFromDataSource(event.getProperty().getValue()));
}
@Override
@@ -1071,25 +1185,6 @@ public abstract class AbstractField<T> extends AbstractComponent implements
super.focus();
}
- /**
- * Creates abstract field by the type of the property.
- *
- * <p>
- * This returns most suitable field type for editing property of given type.
- * </p>
- *
- * @param propertyType
- * the Type of the property, that needs to be edited.
- * @deprecated use e.g.
- * {@link DefaultFieldFactory#createFieldByPropertyType(Class)}
- * instead
- */
- @Deprecated
- public static AbstractField constructField(Class<?> propertyType) {
- return (AbstractField) DefaultFieldFactory
- .createFieldByPropertyType(propertyType);
- }
-
/*
* (non-Javadoc)
*
@@ -1118,8 +1213,8 @@ public abstract class AbstractField<T> extends AbstractComponent implements
* @param newValue
* the new value to be set.
*/
- protected void setInternalValue(Object newValue) {
- value = (T) newValue;
+ protected void setInternalValue(T newValue) {
+ value = newValue;
if (validators != null && !validators.isEmpty()) {
requestRepaint();
}
@@ -1190,7 +1285,7 @@ public abstract class AbstractField<T> extends AbstractComponent implements
* also treats empty string as "empty".
*/
protected boolean isEmpty() {
- return (getValue() == null);
+ return (getFieldValue() == null);
}
/**
@@ -1293,4 +1388,32 @@ public abstract class AbstractField<T> extends AbstractComponent implements
focusable.focus();
}
}
+
+ /**
+ * Gets the converter used to convert the property data source value to the
+ * field value.
+ *
+ * @return The converter or null if none is set.
+ */
+ public Converter<Object, T> getValueConverter() {
+ return valueConverter;
+ }
+
+ /**
+ * Sets the converter used to convert the property data source value to the
+ * field value. The converter must have a target type that matches the field
+ * type.
+ *
+ * The source for the converter is the data model and the target is the
+ * field.
+ *
+ * @param valueConverter
+ * The new value converter to use.
+ */
+ public void setValueConverter(Converter<?, T> valueConverter) {
+ //
+ this.valueConverter = (Converter<Object, T>) valueConverter;
+ requestRepaint();
+ }
+
}
diff --git a/src/com/vaadin/ui/AbstractTextField.java b/src/com/vaadin/ui/AbstractTextField.java
index 1bcb794eba..9eb2ff076d 100644
--- a/src/com/vaadin/ui/AbstractTextField.java
+++ b/src/com/vaadin/ui/AbstractTextField.java
@@ -456,7 +456,7 @@ public abstract class AbstractTextField extends AbstractField<String> implements
}
@Override
- protected void setInternalValue(Object newValue) {
+ protected void setInternalValue(String newValue) {
if (changingVariables && !textChangeEventPending) {
/*
diff --git a/src/com/vaadin/ui/Button.java b/src/com/vaadin/ui/Button.java
index 93bd6d8963..eb580efe31 100644
--- a/src/com/vaadin/ui/Button.java
+++ b/src/com/vaadin/ui/Button.java
@@ -525,12 +525,10 @@ public class Button extends AbstractComponent implements
requestRepaint();
}
- @Override
public int getTabIndex() {
return tabIndex;
}
- @Override
public void setTabIndex(int tabIndex) {
this.tabIndex = tabIndex;
diff --git a/src/com/vaadin/ui/DateField.java b/src/com/vaadin/ui/DateField.java
index 4d83502f65..2daac4a613 100644
--- a/src/com/vaadin/ui/DateField.java
+++ b/src/com/vaadin/ui/DateField.java
@@ -479,7 +479,7 @@ public class DateField extends AbstractField<Date> implements
* @see com.vaadin.ui.AbstractField#setValue(java.lang.Object, boolean)
*/
@Override
- protected void setValue(Object newValue, boolean repaintIsNotNeeded)
+ protected void setValue(Date newValue, boolean repaintIsNotNeeded)
throws Property.ReadOnlyException, Property.ConversionException {
/*
@@ -564,24 +564,8 @@ public class DateField extends AbstractField<Date> implements
}
}
- /**
- * Sets the DateField datasource. Datasource type must assignable to Date.
- *
- * @see com.vaadin.data.Property.Viewer#setPropertyDataSource(Property)
- */
- @Override
- public void setPropertyDataSource(Property newDataSource) {
- if (newDataSource == null
- || Date.class.isAssignableFrom(newDataSource.getType())) {
- super.setPropertyDataSource(newDataSource);
- } else {
- throw new IllegalArgumentException(
- "DateField only supports Date properties");
- }
- }
-
@Override
- protected void setInternalValue(Object newValue) {
+ protected void setInternalValue(Date newValue) {
// Also set the internal dateString
if (newValue != null) {
dateString = newValue.toString();