summaryrefslogtreecommitdiffstats
path: root/server/src
diff options
context:
space:
mode:
authorDenis Anisimov <denis@vaadin.com>2016-09-27 10:41:37 +0300
committerDenis Anisimov <denis@vaadin.com>2016-09-30 06:56:18 +0000
commitd36d63fefa3ab9f4908f97772bcc6499e4f52532 (patch)
treed2189db8f44601925a96435dedec031459eeed57 /server/src
parentdb3a91c9b4d8d0451c8f1c5e875f4c6f6f50324a (diff)
downloadvaadin-framework-d36d63fefa3ab9f4908f97772bcc6499e4f52532.tar.gz
vaadin-framework-d36d63fefa3ab9f4908f97772bcc6499e4f52532.zip
Make AbstractDateField based on LocalDate (#125).
Change-Id: I33a4a4f0f3437a8d1733031a131afbe844c12afb
Diffstat (limited to 'server/src')
-rw-r--r--server/src/main/java/com/vaadin/ui/AbstractDateField.java314
-rw-r--r--server/src/main/java/com/vaadin/ui/DateField.java24
-rw-r--r--server/src/main/java/com/vaadin/ui/InlineDateField.java12
-rw-r--r--server/src/main/java/com/vaadin/ui/declarative/DesignFormatter.java3
-rw-r--r--server/src/main/java/com/vaadin/ui/declarative/converters/DesignLocalDateConverter.java60
-rw-r--r--server/src/test/java/com/vaadin/data/BinderBookOfVaadinTest.java48
-rw-r--r--server/src/test/java/com/vaadin/tests/server/component/datefield/DateFieldDeclarativeTest.java13
-rw-r--r--server/src/test/java/com/vaadin/tests/server/component/datefield/InlineDateFieldDeclarativeTest.java15
-rw-r--r--server/src/test/java/com/vaadin/tests/server/component/datefield/ResolutionTest.java23
-rw-r--r--server/src/test/java/com/vaadin/ui/DateFieldTestCase.java35
10 files changed, 222 insertions, 325 deletions
diff --git a/server/src/main/java/com/vaadin/ui/AbstractDateField.java b/server/src/main/java/com/vaadin/ui/AbstractDateField.java
index ab0aed1b0c..6ac135b354 100644
--- a/server/src/main/java/com/vaadin/ui/AbstractDateField.java
+++ b/server/src/main/java/com/vaadin/ui/AbstractDateField.java
@@ -16,20 +16,21 @@
package com.vaadin.ui;
import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneOffset;
import java.util.Calendar;
-import java.util.Comparator;
import java.util.Date;
import java.util.EventObject;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
-import java.util.TimeZone;
import java.util.logging.Logger;
import org.jsoup.nodes.Element;
import com.vaadin.data.Result;
-import com.vaadin.data.validator.RangeValidator;
+import com.vaadin.data.validator.DateRangeValidator;
import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener;
import com.vaadin.event.FieldEvents.BlurNotifier;
@@ -47,20 +48,20 @@ import com.vaadin.ui.declarative.DesignAttributeHandler;
import com.vaadin.ui.declarative.DesignContext;
/**
- * A date editor component with <code>java.util.Date</code> as an input value.
+ * A date editor component with {@link LocalDate} as an input value.
*
* @author Vaadin Ltd
*
* @since 8.0
*
*/
-public abstract class AbstractDateField extends AbstractField<Date>
+public abstract class AbstractDateField extends AbstractField<LocalDate>
implements LegacyComponent, FocusNotifier, BlurNotifier {
/**
* Value of the field.
*/
- private Date value;
+ private LocalDate value;
/**
* Specified smallest modifiable unit for the date field.
@@ -68,11 +69,6 @@ public abstract class AbstractDateField extends AbstractField<Date>
private Resolution resolution = Resolution.DAY;
/**
- * The internal calendar to be used in java.utl.Date conversions.
- */
- private transient Calendar calendar;
-
- /**
* Overridden format string
*/
private String dateFormat;
@@ -96,8 +92,6 @@ public abstract class AbstractDateField extends AbstractField<Date>
private String defaultParseErrorMessage = "Date format not recognized";
- private TimeZone timeZone = null;
-
private static Map<Resolution, String> variableNameForResolution = new HashMap<>();
private String dateOutOfRangeMessage = "Date is out of allowed range";
@@ -110,9 +104,6 @@ public abstract class AbstractDateField extends AbstractField<Date>
private boolean preventValueChangeEvent;
static {
- variableNameForResolution.put(Resolution.SECOND, "sec");
- variableNameForResolution.put(Resolution.MINUTE, "min");
- variableNameForResolution.put(Resolution.HOUR, "hour");
variableNameForResolution.put(Resolution.DAY, "day");
variableNameForResolution.put(Resolution.MONTH, "month");
variableNameForResolution.put(Resolution.YEAR, "year");
@@ -143,9 +134,9 @@ public abstract class AbstractDateField extends AbstractField<Date>
* @param caption
* the caption <code>String</code> for the editor.
* @param value
- * the Date value.
+ * the LocalDate value.
*/
- public AbstractDateField(String caption, Date value) {
+ public AbstractDateField(String caption, LocalDate value) {
setValue(value);
setCaption(caption);
}
@@ -166,7 +157,7 @@ public abstract class AbstractDateField extends AbstractField<Date>
}
if (getDateFormat() != null) {
- target.addAttribute("format", dateFormat);
+ target.addAttribute("format", getDateFormat());
}
if (!isLenient()) {
@@ -181,9 +172,7 @@ public abstract class AbstractDateField extends AbstractField<Date>
* app or refresh.
*/
- // Gets the calendar
- final Calendar calendar = getCalendar();
- final Date currentDate = getValue();
+ final LocalDate currentDate = getValue();
// Only paint variables for the resolution and up, e.g. Resolution DAY
// paints DAY,MONTH,YEAR
@@ -191,11 +180,7 @@ public abstract class AbstractDateField extends AbstractField<Date>
.getResolutionsHigherOrEqualTo(resolution)) {
int value = -1;
if (currentDate != null) {
- value = calendar.get(res.getCalendarField());
- if (res == Resolution.MONTH) {
- // Calendar month is zero based
- value++;
- }
+ value = getDateValue(currentDate, res);
}
target.addVariable(this, variableNameForResolution.get(res), value);
}
@@ -211,14 +196,12 @@ public abstract class AbstractDateField extends AbstractField<Date>
if (!isReadOnly() && (variables.containsKey("year")
|| variables.containsKey("month")
- || variables.containsKey("day") || variables.containsKey("hour")
- || variables.containsKey("min") || variables.containsKey("sec")
- || variables.containsKey("msec")
+ || variables.containsKey("day")
|| variables.containsKey("dateString"))) {
// Old and new dates
- final Date oldDate = getValue();
- Date newDate = null;
+ final LocalDate oldDate = getValue();
+ LocalDate newDate = null;
// this enables analyzing invalid input on the server
final String newDateString = (String) variables.get("dateString");
@@ -226,55 +209,37 @@ public abstract class AbstractDateField extends AbstractField<Date>
// Gets the new date in parts
boolean hasChanges = false;
- Map<Resolution, Integer> calendarFieldChanges = new HashMap<>();
+ Map<Resolution, Integer> calendarFields = new HashMap<>();
- for (Resolution r : Resolution
- .getResolutionsHigherOrEqualTo(resolution)) {
+ for (Resolution resolution : Resolution
+ .getResolutionsHigherOrEqualTo(getResolution())) {
// Only handle what the client is allowed to send. The same
// resolutions that are painted
- String variableName = variableNameForResolution.get(r);
+ String variableName = variableNameForResolution.get(resolution);
+ Integer value = getDateValue(oldDate, resolution);
if (variables.containsKey(variableName)) {
- Integer value = (Integer) variables.get(variableName);
- if (r == Resolution.MONTH) {
- // Calendar MONTH is zero based
- value--;
- }
- if (value >= 0) {
+ Integer newValue = (Integer) variables.get(variableName);
+ if (newValue >= 0) {
hasChanges = true;
- calendarFieldChanges.put(r, value);
+ value = newValue;
}
}
+ calendarFields.put(resolution, value);
}
// If no new variable values were received, use the previous value
if (!hasChanges) {
newDate = null;
} else {
- // Clone the calendar for date operation
- final Calendar cal = getCalendar();
-
- // Update the value based on the received info
- // Must set in this order to avoid invalid dates (or wrong
- // dates if lenient is true) in calendar
- for (int r = Resolution.YEAR.ordinal(); r >= 0; r--) {
- Resolution res = Resolution.values()[r];
- if (calendarFieldChanges.containsKey(res)) {
-
- // Field resolution should be included. Others are
- // skipped so that client can not make unexpected
- // changes (e.g. day change even though resolution is
- // year).
- Integer newValue = calendarFieldChanges.get(res);
- cal.set(res.getCalendarField(), newValue);
- }
- }
- newDate = cal.getTime();
+ newDate = LocalDate.of(calendarFields.get(Resolution.YEAR),
+ calendarFields.getOrDefault(Resolution.MONTH, 1),
+ calendarFields.getOrDefault(Resolution.DAY, 1));
}
if (newDate == null && dateString != null
&& !dateString.isEmpty()) {
- Result<Date> parsedDate = handleUnparsableDateString(
+ Result<LocalDate> parsedDate = handleUnparsableDateString(
dateString);
if (parsedDate.isError()) {
@@ -365,17 +330,15 @@ public abstract class AbstractDateField extends AbstractField<Date>
* @param startDate
* - the allowed range's start date
*/
- public void setRangeStart(Date startDate) {
- if (startDate != null && getState().rangeEnd != null
- && startDate.after(getState().rangeEnd)) {
+ public void setRangeStart(LocalDate startDate) {
+ Date date = convertLocalDate(startDate);
+ if (date != null && getState().rangeEnd != null
+ && date.after(getState().rangeEnd)) {
throw new IllegalStateException(
"startDate cannot be later than endDate");
}
- // Create a defensive copy against issues when using java.sql.Date (and
- // also against mutable Date).
- getState().rangeStart = startDate != null
- ? new Date(startDate.getTime()) : null;
+ getState().rangeStart = date;
}
/**
@@ -431,35 +394,33 @@ public abstract class AbstractDateField extends AbstractField<Date>
* - the allowed range's end date (inclusive, based on the
* current resolution)
*/
- public void setRangeEnd(Date endDate) {
- if (endDate != null && getState().rangeStart != null
- && getState().rangeStart.after(endDate)) {
+ public void setRangeEnd(LocalDate endDate) {
+ Date date = convertLocalDate(endDate);
+ if (date != null && getState().rangeStart != null
+ && getState().rangeStart.after(date)) {
throw new IllegalStateException(
"endDate cannot be earlier than startDate");
}
- // Create a defensive copy against issues when using java.sql.Date (and
- // also against mutable Date).
- getState().rangeEnd = endDate != null ? new Date(endDate.getTime())
- : null;
+ getState().rangeEnd = date;
}
/**
* Returns the precise rangeStart used.
*
- * @return the precise rangeStart used
+ * @return the precise rangeStart used, may be null.
*/
- public Date getRangeStart() {
- return getState(false).rangeStart;
+ public LocalDate getRangeStart() {
+ return convertDate(getState(false).rangeStart);
}
/**
* Returns the precise rangeEnd used.
*
- * @return the precise rangeEnd used
+ * @return the precise rangeEnd used, may be null.
*/
- public Date getRangeEnd() {
- return getState(false).rangeEnd;
+ public LocalDate getRangeEnd() {
+ return convertDate(getState(false).rangeEnd);
}
/**
@@ -519,12 +480,12 @@ public abstract class AbstractDateField extends AbstractField<Date>
}
@Override
- public Date getValue() {
+ public LocalDate getValue() {
return value;
}
@Override
- public void setValue(Date value) {
+ public void setValue(LocalDate value) {
/*
* First handle special case when the client side component have a date
* string but value is null (e.g. unparsable date string typed in by the
@@ -567,64 +528,6 @@ public abstract class AbstractDateField extends AbstractField<Date>
}
/**
- * Returns new instance calendar used in Date conversions.
- *
- * Returns new clone of the calendar object initialized using the the
- * current date (if available)
- *
- * If this is no calendar is assigned the <code>Calendar.getInstance</code>
- * is used.
- *
- * @return the Calendar.
- * @see #setCalendar(Calendar)
- */
- private Calendar getCalendar() {
-
- // Makes sure we have an calendar instance
- if (calendar == null) {
- calendar = Calendar.getInstance();
- // Start by a zeroed calendar to avoid having values for lower
- // resolution variables e.g. time when resolution is day
- int min, field;
- for (Resolution r : Resolution
- .getResolutionsLowerThan(resolution)) {
- field = r.getCalendarField();
- min = calendar.getActualMinimum(field);
- calendar.set(field, min);
- }
- calendar.set(Calendar.MILLISECOND, 0);
- }
-
- // Clone the instance
- final Calendar newCal = (Calendar) calendar.clone();
-
- final TimeZone currentTimeZone = getTimeZone();
- if (currentTimeZone != null) {
- newCal.setTimeZone(currentTimeZone);
- }
-
- final Date currentDate = getValue();
- if (currentDate != null) {
- newCal.setTime(currentDate);
- }
- return newCal;
- }
-
- /**
- * Gets the time zone used by this field. The time zone is used to convert
- * the absolute time in a Date object to a logical time displayed in the
- * selector and to convert the select time back to a Date object.
- *
- * If {@code null} is returned, the current default time zone returned by
- * {@code TimeZone.getDefault()} is used.
- *
- * @return the current time zone
- */
- public TimeZone getTimeZone() {
- return timeZone;
- }
-
- /**
* Return the error message that is shown if the user inputted value can't
* be parsed into a Date object. If
* {@link #handleUnparsableDateString(String)} is overridden and it throws a
@@ -655,23 +558,6 @@ public abstract class AbstractDateField extends AbstractField<Date>
defaultParseErrorMessage = parsingErrorMessage;
}
- /**
- * Sets the time zone used by this date field. The time zone is used to
- * convert the absolute time in a Date object to a logical time displayed in
- * the selector and to convert the select time back to a Date object.
- *
- * If no time zone has been set, the current default time zone returned by
- * {@code TimeZone.getDefault()} is used.
- *
- * @see #getTimeZone()
- * @param timeZone
- * the time zone to use for time calculations.
- */
- public void setTimeZone(TimeZone timeZone) {
- this.timeZone = timeZone;
- markAsDirty();
- }
-
@Override
public Registration addFocusListener(FocusListener listener) {
addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener,
@@ -704,14 +590,14 @@ public abstract class AbstractDateField extends AbstractField<Date>
public void readDesign(Element design, DesignContext designContext) {
super.readDesign(design, designContext);
if (design.hasAttr("value") && !design.attr("value").isEmpty()) {
- Date date = DesignAttributeHandler.getFormatter()
- .parse(design.attr("value"), Date.class);
+ LocalDate date = DesignAttributeHandler.getFormatter()
+ .parse(design.attr("value"), LocalDate.class);
// formatting will return null if it cannot parse the string
if (date == null) {
Logger.getLogger(AbstractDateField.class.getName()).info(
"cannot parse " + design.attr("value") + " as date");
}
- setValue(date);
+ doSetValue(date);
}
}
@@ -750,7 +636,7 @@ public abstract class AbstractDateField extends AbstractField<Date>
* date string to handle
* @return result that contains parsed Date as a value or an error
*/
- protected Result<Date> handleUnparsableDateString(String dateString) {
+ protected Result<LocalDate> handleUnparsableDateString(String dateString) {
return Result.error(getParseErrorMessage());
}
@@ -765,7 +651,7 @@ public abstract class AbstractDateField extends AbstractField<Date>
}
@Override
- protected void doSetValue(Date value) {
+ protected void doSetValue(LocalDate value) {
// Also set the internal dateString
if (value != null) {
dateString = value.toString();
@@ -780,87 +666,61 @@ public abstract class AbstractDateField extends AbstractField<Date>
uiHasValidDateString = true;
setComponentError(new UserError(currentParseErrorMessage));
} else {
- RangeValidator<Date> validator = new RangeValidator<>(
- getDateOutOfRangeMessage(), Comparator.naturalOrder(),
- getRangeStart(getResolution()),
- getRangeEnd(getResolution()));
- Result<Date> result = validator.apply(value);
+ DateRangeValidator validator = new DateRangeValidator(
+ getDateOutOfRangeMessage(),
+ getDate(getRangeStart(), getResolution()),
+ getDate(getRangeEnd(), getResolution()));
+ Result<LocalDate> result = validator.apply(value);
if (result.isError()) {
setComponentError(new UserError(getDateOutOfRangeMessage()));
}
}
}
- /**
- * Gets the start range for a certain resolution. The range is inclusive, so
- * if <code>rangeStart</code> is set to one millisecond before year n and
- * resolution is set to YEAR, any date in year n - 1 will be accepted.
- * Lowest supported resolution is DAY.
- *
- * @param forResolution
- * - the range conforms to the resolution
- * @return
- */
- private Date getRangeStart(Resolution forResolution) {
- if (getState(false).rangeStart == null) {
+ private LocalDate getDate(LocalDate date, Resolution forResolution) {
+ if (date == null) {
return null;
}
- Calendar startCal = Calendar.getInstance();
- startCal.setTime(getState(false).rangeStart);
-
if (forResolution == Resolution.YEAR) {
- startCal.set(startCal.get(Calendar.YEAR), 0, 1, 0, 0, 0);
+ return date.withDayOfYear(1);
} else if (forResolution == Resolution.MONTH) {
- startCal.set(startCal.get(Calendar.YEAR),
- startCal.get(Calendar.MONTH), 1, 0, 0, 0);
+ return date.withDayOfMonth(1);
} else {
- startCal.set(startCal.get(Calendar.YEAR),
- startCal.get(Calendar.MONTH), startCal.get(Calendar.DATE),
- 0, 0, 0);
+ return date;
}
-
- startCal.set(Calendar.MILLISECOND, 0);
- return startCal.getTime();
}
- /**
- * Gets the end range for a certain resolution. The range is inclusive, so
- * if rangeEnd is set to zero milliseconds past year n and resolution is set
- * to YEAR, any date in year n will be accepted. Resolutions lower than DAY
- * will be interpreted on a DAY level. That is, everything below DATE is
- * cleared
- *
- * @param forResolution
- * - the range conforms to the resolution
- * @return
- */
- private Date getRangeEnd(Resolution forResolution) {
- // We need to set the correct resolution for the dates,
- // otherwise the range validator will complain
+ private int getDateValue(LocalDate date, Resolution resolution) {
+ LocalDate value = date;
+ if (value == null) {
+ value = LocalDate.of(1, 1, 1);
+ }
+ switch (resolution) {
+ case DAY:
+ return value.getDayOfMonth();
+ case MONTH:
+ return value.getMonthValue();
+ case YEAR:
+ return value.getYear();
+ default:
+ assert false : "Unexpected resolution argument " + resolution;
+ return -1;
+ }
+ }
- Date rangeEnd = getState(false).rangeEnd;
- if (rangeEnd == null) {
+ private Date convertLocalDate(LocalDate date) {
+ if (date == null) {
return null;
}
+ return Date.from(date.atStartOfDay(ZoneOffset.UTC).toInstant());
+ }
- Calendar endCal = Calendar.getInstance();
- endCal.setTime(rangeEnd);
-
- if (forResolution == Resolution.YEAR) {
- // Adding one year (minresolution) and clearing the rest.
- endCal.set(endCal.get(Calendar.YEAR) + 1, 0, 1, 0, 0, 0);
- } else if (forResolution == Resolution.MONTH) {
- // Adding one month (minresolution) and clearing the rest.
- endCal.set(endCal.get(Calendar.YEAR),
- endCal.get(Calendar.MONTH) + 1, 1, 0, 0, 0);
- } else {
- endCal.set(endCal.get(Calendar.YEAR), endCal.get(Calendar.MONTH),
- endCal.get(Calendar.DATE) + 1, 0, 0, 0);
+ private LocalDate convertDate(Date date) {
+ if (date == null) {
+ return null;
}
- // removing one millisecond will now get the endDate to return to
- // current resolution's set time span (year or month)
- endCal.set(Calendar.MILLISECOND, -1);
- return endCal.getTime();
+ return Instant.ofEpochMilli(date.getTime()).atZone(ZoneOffset.UTC)
+ .toLocalDate();
}
}
diff --git a/server/src/main/java/com/vaadin/ui/DateField.java b/server/src/main/java/com/vaadin/ui/DateField.java
index 00e71a6eff..04b32f66a0 100644
--- a/server/src/main/java/com/vaadin/ui/DateField.java
+++ b/server/src/main/java/com/vaadin/ui/DateField.java
@@ -15,11 +15,11 @@
*/
package com.vaadin.ui;
-import java.util.Date;
+import java.time.LocalDate;
import com.vaadin.server.PaintException;
import com.vaadin.server.PaintTarget;
-import com.vaadin.shared.ui.datefield.PopupDateFieldState;
+import com.vaadin.shared.ui.datefield.DateFieldState;
/**
* A date entry component, which displays the actual date selector as a popup.
@@ -34,27 +34,27 @@ public class DateField extends AbstractDateField {
private String inputPrompt = null;
/**
- * Constructs an empty <code>PopupDateField</code> with no caption.
+ * Constructs an empty <code>DateField</code> with no caption.
*/
public DateField() {
super();
}
/**
- * Constructs a new <code>PopupDateField</code> with the given caption and
+ * Constructs a new <code>DateField</code> with the given caption and
* initial text contents.
*
* @param caption
* the caption <code>String</code> for the editor.
* @param value
- * the Date value.
+ * the LocalDate value.
*/
- public DateField(String caption, Date value) {
+ public DateField(String caption, LocalDate value) {
super(caption, value);
}
/**
- * Constructs an empty <code>PopupDateField</code> with caption.
+ * Constructs an empty <code>DateField</code> with caption.
*
* @param caption
* the caption of the datefield.
@@ -94,19 +94,19 @@ public class DateField extends AbstractDateField {
}
@Override
- protected PopupDateFieldState getState() {
- return (PopupDateFieldState) super.getState();
+ protected DateFieldState getState() {
+ return (DateFieldState) super.getState();
}
@Override
- protected PopupDateFieldState getState(boolean markAsDirty) {
- return (PopupDateFieldState) super.getState(markAsDirty);
+ protected DateFieldState getState(boolean markAsDirty) {
+ return (DateFieldState) super.getState(markAsDirty);
}
/**
* Checks whether the text field is enabled (default) or not.
*
- * @see PopupDateField#setTextFieldEnabled(boolean);
+ * @see #setTextFieldEnabled(boolean)
*
* @return <b>true</b> if the text field is enabled, <b>false</b> otherwise.
*/
diff --git a/server/src/main/java/com/vaadin/ui/InlineDateField.java b/server/src/main/java/com/vaadin/ui/InlineDateField.java
index 8fd6656c2f..a1610949f1 100644
--- a/server/src/main/java/com/vaadin/ui/InlineDateField.java
+++ b/server/src/main/java/com/vaadin/ui/InlineDateField.java
@@ -15,7 +15,7 @@
*/
package com.vaadin.ui;
-import java.util.Date;
+import java.time.LocalDate;
/**
* A date entry component, which displays the actual date selector inline.
@@ -28,27 +28,27 @@ import java.util.Date;
public class InlineDateField extends AbstractDateField {
/**
- * Constructs an empty <code>DateField</code> with no caption.
+ * Constructs an empty <code>InlineDateField</code> with no caption.
*/
public InlineDateField() {
super();
}
/**
- * Constructs a new <code>DateField</code> with the given caption and
+ * Constructs a new <code>InlineDateField</code> with the given caption and
* initial text contents.
*
* @param caption
* the caption <code>String</code> for the editor.
* @param value
- * the Date value.
+ * the LocalDate value.
*/
- public InlineDateField(String caption, Date value) {
+ public InlineDateField(String caption, LocalDate value) {
super(caption, value);
}
/**
- * Constructs an empty <code>DateField</code> with caption.
+ * Constructs an empty <code>InlineDateField</code> with caption.
*
* @param caption
* the caption of the datefield.
diff --git a/server/src/main/java/com/vaadin/ui/declarative/DesignFormatter.java b/server/src/main/java/com/vaadin/ui/declarative/DesignFormatter.java
index 203954dd27..64430470d4 100644
--- a/server/src/main/java/com/vaadin/ui/declarative/DesignFormatter.java
+++ b/server/src/main/java/com/vaadin/ui/declarative/DesignFormatter.java
@@ -20,6 +20,7 @@ import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
+import java.time.LocalDate;
import java.util.Collections;
import java.util.Date;
import java.util.Locale;
@@ -39,6 +40,7 @@ import com.vaadin.event.ShortcutAction;
import com.vaadin.server.Resource;
import com.vaadin.ui.declarative.converters.DesignDateConverter;
import com.vaadin.ui.declarative.converters.DesignEnumConverter;
+import com.vaadin.ui.declarative.converters.DesignLocalDateConverter;
import com.vaadin.ui.declarative.converters.DesignObjectConverter;
import com.vaadin.ui.declarative.converters.DesignResourceConverter;
import com.vaadin.ui.declarative.converters.DesignShortcutActionConverter;
@@ -170,6 +172,7 @@ public class DesignFormatter implements Serializable {
converterMap.put(char.class, charConverter);
converterMap.put(Date.class, new DesignDateConverter());
+ converterMap.put(LocalDate.class, new DesignLocalDateConverter());
converterMap.put(ShortcutAction.class,
new DesignShortcutActionConverter());
converterMap.put(Resource.class, new DesignResourceConverter());
diff --git a/server/src/main/java/com/vaadin/ui/declarative/converters/DesignLocalDateConverter.java b/server/src/main/java/com/vaadin/ui/declarative/converters/DesignLocalDateConverter.java
new file mode 100644
index 0000000000..188516b431
--- /dev/null
+++ b/server/src/main/java/com/vaadin/ui/declarative/converters/DesignLocalDateConverter.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * 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 com.vaadin.ui.declarative.converters;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.Locale;
+
+import com.vaadin.data.Result;
+import com.vaadin.data.util.converter.Converter;
+import com.vaadin.ui.declarative.DesignAttributeHandler;
+
+/**
+ * A {@link LocalDate} converter to be used by {@link DesignAttributeHandler}.
+ * Provides ISO-compliant way of storing date and time.
+ *
+ * @since 8.0
+ * @author Vaadin Ltd
+ */
+public class DesignLocalDateConverter implements Converter<String, LocalDate> {
+
+ @Override
+ public Result<LocalDate> convertToModel(String value, Locale locale) {
+ for (String pattern : new String[] { "yyyy-MM-dd", "yyyy-MM",
+ "yyyy" }) {
+ try {
+ Locale effectiveLocale = locale == null ? Locale.ENGLISH
+ : locale;
+ LocalDate date = DateTimeFormatter
+ .ofPattern(pattern, effectiveLocale)
+ .parse(value, LocalDate::from);
+ return Result.ok(date);
+ } catch (DateTimeParseException e) {
+ // not parseable, ignore and try another format
+ }
+ }
+ return Result.error("Could not parse date value: " + value);
+ }
+
+ @Override
+ public String convertToPresentation(LocalDate value, Locale locale) {
+ return DateTimeFormatter.ofPattern("yyyy-MM-dd",
+ locale == null ? Locale.ENGLISH : locale).format(value);
+ }
+
+}
diff --git a/server/src/test/java/com/vaadin/data/BinderBookOfVaadinTest.java b/server/src/test/java/com/vaadin/data/BinderBookOfVaadinTest.java
index 41c9806165..0b55bc8cad 100644
--- a/server/src/test/java/com/vaadin/data/BinderBookOfVaadinTest.java
+++ b/server/src/test/java/com/vaadin/data/BinderBookOfVaadinTest.java
@@ -15,8 +15,7 @@
*/
package com.vaadin.data;
-import java.util.Calendar;
-import java.util.Date;
+import java.time.LocalDate;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicReference;
@@ -33,9 +32,9 @@ import com.vaadin.data.util.converter.StringToIntegerConverter;
import com.vaadin.data.validator.EmailValidator;
import com.vaadin.server.AbstractErrorMessage;
import com.vaadin.ui.Button;
+import com.vaadin.ui.DateField;
import com.vaadin.ui.Label;
import com.vaadin.ui.Notification;
-import com.vaadin.ui.DateField;
import com.vaadin.ui.Slider;
import com.vaadin.ui.TextField;
@@ -116,13 +115,13 @@ public class BinderBookOfVaadinTest {
}
public static class Trip {
- private Date returnDate;
+ private LocalDate returnDate;
- public Date getReturnDate() {
+ public LocalDate getReturnDate() {
return returnDate;
}
- public void setReturnDate(Date returnDate) {
+ public void setReturnDate(LocalDate returnDate) {
this.returnDate = returnDate;
}
}
@@ -304,20 +303,18 @@ public class BinderBookOfVaadinTest {
DateField departing = new DateField("Departing");
DateField returning = new DateField("Returning");
- Binding<Trip, Date, Date> returnBinding = binder.forField(returning)
- .withValidator(
- returnDate -> !returnDate.before(departing.getValue()),
+ Binding<Trip, LocalDate, LocalDate> returnBinding = binder
+ .forField(returning).withValidator(
+ returnDate -> !returnDate
+ .isBefore(departing.getValue()),
"Cannot return before departing");
returnBinding.bind(Trip::getReturnDate, Trip::setReturnDate);
departing.addValueChangeListener(event -> returnBinding.validate());
- Calendar calendar = Calendar.getInstance();
- Date past = calendar.getTime();
- calendar.add(1, Calendar.DAY_OF_YEAR);
- Date before = calendar.getTime();
- calendar.add(1, Calendar.DAY_OF_YEAR);
- Date after = calendar.getTime();
+ LocalDate past = LocalDate.now();
+ LocalDate before = past.plusDays(1);
+ LocalDate after = before.plusDays(1);
departing.setValue(before);
returning.setValue(after);
@@ -359,25 +356,23 @@ public class BinderBookOfVaadinTest {
DateField departing = new DateField("Departing");
DateField returning = new DateField("Returning");
- Binding<Trip, Date, Date> returnBinding = binder.forField(returning)
- .withValidator(
- returnDate -> !returnDate.before(departing.getValue()),
+ Binding<Trip, LocalDate, LocalDate> returnBinding = binder
+ .forField(returning).withValidator(
+ returnDate -> !returnDate
+ .isBefore(departing.getValue()),
"Cannot return before departing");
returnBinding.bind(Trip::getReturnDate, Trip::setReturnDate);
departing.addValueChangeListener(event -> returnBinding.validate());
- Calendar calendar = Calendar.getInstance();
- Date past = calendar.getTime();
- calendar.add(1, Calendar.DAY_OF_YEAR);
- Date before = calendar.getTime();
- calendar.add(1, Calendar.DAY_OF_YEAR);
- Date after = calendar.getTime();
+ LocalDate past = LocalDate.now();
+ LocalDate before = past.plusDays(1);
+ LocalDate after = before.plusDays(1);
departing.setValue(before);
returning.setValue(after);
- ValidationStatus<Date> result = returnBinding.validate();
+ ValidationStatus<LocalDate> result = returnBinding.validate();
Assert.assertFalse(result.isError());
Assert.assertNull(departing.getComponentError());
@@ -465,8 +460,7 @@ public class BinderBookOfVaadinTest {
@Test
public void binder_saveIfValid() {
- BeanBinder<BookPerson> binder = new BeanBinder<BookPerson>(
- BookPerson.class);
+ BeanBinder<BookPerson> binder = new BeanBinder<>(BookPerson.class);
// Phone or email has to be specified for the bean
Validator<BookPerson> phoneOrEmail = Validator.from(
diff --git a/server/src/test/java/com/vaadin/tests/server/component/datefield/DateFieldDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/datefield/DateFieldDeclarativeTest.java
index 9d6e57b207..0448bf5697 100644
--- a/server/src/test/java/com/vaadin/tests/server/component/datefield/DateFieldDeclarativeTest.java
+++ b/server/src/test/java/com/vaadin/tests/server/component/datefield/DateFieldDeclarativeTest.java
@@ -15,11 +15,10 @@
*/
package com.vaadin.tests.server.component.datefield;
-import java.util.Date;
+import java.time.LocalDate;
import org.junit.Test;
-import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.tests.design.DeclarativeTestBase;
import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.DateField;
@@ -31,20 +30,18 @@ import com.vaadin.ui.DateField;
* @since 7.4
* @author Vaadin Ltd
*/
-public class DateFieldDeclarativeTest
- extends DeclarativeTestBase<DateField> {
+public class DateFieldDeclarativeTest extends DeclarativeTestBase<DateField> {
private String getBasicDesign() {
- return "<vaadin-date-field assistive-text='at' text-field-enabled='false' show-iso-week-numbers resolution=\"MINUTE\" range-end=\"2019-01-15\" input-prompt=\"Pick a day\" value=\"2003-02-27 07:15\"></vaadin-date-field>";
+ return "<vaadin-date-field assistive-text='at' text-field-enabled='false' show-iso-week-numbers range-end=\"2019-01-15\" input-prompt=\"Pick a day\" value=\"2003-02-27\"></vaadin-date-field>";
}
private DateField getBasicExpected() {
DateField pdf = new DateField();
pdf.setShowISOWeekNumbers(true);
- pdf.setResolution(Resolution.MINUTE);
- pdf.setRangeEnd(new Date(2019 - 1900, 1 - 1, 15));
+ pdf.setRangeEnd(LocalDate.of(2019, 01, 15));
pdf.setInputPrompt("Pick a day");
- pdf.setValue(new Date(2003 - 1900, 2 - 1, 27, 7, 15));
+ pdf.setValue(LocalDate.of(2003, 2, 27));
pdf.setTextFieldEnabled(false);
pdf.setAssistiveText("at");
return pdf;
diff --git a/server/src/test/java/com/vaadin/tests/server/component/datefield/InlineDateFieldDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/datefield/InlineDateFieldDeclarativeTest.java
index 70309c7e7d..c947641986 100644
--- a/server/src/test/java/com/vaadin/tests/server/component/datefield/InlineDateFieldDeclarativeTest.java
+++ b/server/src/test/java/com/vaadin/tests/server/component/datefield/InlineDateFieldDeclarativeTest.java
@@ -17,18 +17,18 @@ package com.vaadin.tests.server.component.datefield;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
-import java.text.SimpleDateFormat;
+import java.time.LocalDate;
import org.junit.Test;
-import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.tests.design.DeclarativeTestBase;
import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.InlineDateField;
import com.vaadin.ui.declarative.Design;
/**
- * Tests the declarative support for implementations of {@link AbstractDateField}.
+ * Tests the declarative support for implementations of
+ * {@link AbstractDateField}.
*
* @since 7.4
* @author Vaadin Ltd
@@ -39,13 +39,10 @@ public class InlineDateFieldDeclarativeTest
@Test
public void testInlineDateFieldToFromDesign() throws Exception {
InlineDateField field = new InlineDateField("Day is",
- new SimpleDateFormat("yyyy-MM-dd").parse("2003-02-27"));
- field.setResolution(Resolution.DAY);
+ LocalDate.of(2003, 2, 27));
field.setShowISOWeekNumbers(true);
- field.setRangeStart(
- new SimpleDateFormat("yyyy-MM-dd").parse("2001-02-27"));
- field.setRangeEnd(
- new SimpleDateFormat("yyyy-MM-dd").parse("2011-02-27"));
+ field.setRangeStart(LocalDate.of(2001, 2, 27));
+ field.setRangeEnd(LocalDate.of(20011, 2, 27));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
Design.write(field, bos);
diff --git a/server/src/test/java/com/vaadin/tests/server/component/datefield/ResolutionTest.java b/server/src/test/java/com/vaadin/tests/server/component/datefield/ResolutionTest.java
index 2fb7702aeb..cb8b6f4914 100644
--- a/server/src/test/java/com/vaadin/tests/server/component/datefield/ResolutionTest.java
+++ b/server/src/test/java/com/vaadin/tests/server/component/datefield/ResolutionTest.java
@@ -31,35 +31,12 @@ public class ResolutionTest {
}
@Test
- public void testResolutionLowerThanDay() {
- Iterable<Resolution> higherOrEqual = Resolution
- .getResolutionsLowerThan(Resolution.DAY);
- ArrayList<Resolution> expected = new ArrayList<>();
- expected.add(Resolution.HOUR);
- expected.add(Resolution.MINUTE);
- expected.add(Resolution.SECOND);
- TestUtil.assertIterableEquals(expected, higherOrEqual);
-
- }
-
- @Test
- public void testResolutionLowerThanSecond() {
- Iterable<Resolution> higherOrEqual = Resolution
- .getResolutionsLowerThan(Resolution.SECOND);
- ArrayList<Resolution> expected = new ArrayList<>();
- TestUtil.assertIterableEquals(expected, higherOrEqual);
- }
-
- @Test
public void testResolutionLowerThanYear() {
Iterable<Resolution> higherOrEqual = Resolution
.getResolutionsLowerThan(Resolution.YEAR);
ArrayList<Resolution> expected = new ArrayList<>();
expected.add(Resolution.MONTH);
expected.add(Resolution.DAY);
- expected.add(Resolution.HOUR);
- expected.add(Resolution.MINUTE);
- expected.add(Resolution.SECOND);
TestUtil.assertIterableEquals(expected, higherOrEqual);
}
diff --git a/server/src/test/java/com/vaadin/ui/DateFieldTestCase.java b/server/src/test/java/com/vaadin/ui/DateFieldTestCase.java
index b7b7dfd9f9..8abb16af6c 100644
--- a/server/src/test/java/com/vaadin/ui/DateFieldTestCase.java
+++ b/server/src/test/java/com/vaadin/ui/DateFieldTestCase.java
@@ -4,21 +4,22 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsNull.nullValue;
-import java.util.Date;
+import java.time.LocalDate;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class DateFieldTestCase {
private AbstractDateField dateField;
- private Date date;
+ private LocalDate date;
@Before
public void setup() {
dateField = new AbstractDateField() {
};
- date = new Date();
+ date = LocalDate.now();
}
@Test
@@ -29,13 +30,17 @@ public class DateFieldTestCase {
}
@Test
- public void rangeStartIsImmutable() {
- long expectedTime = date.getTime();
-
+ public void rangeStartIsAcceptedAsValue() {
dateField.setRangeStart(date);
- date.setTime(expectedTime + 1);
+ dateField.setValue(date);
+ Assert.assertNull(dateField.getComponentError());
+ }
- assertThat(dateField.getRangeStart().getTime(), is(expectedTime));
+ @Test
+ public void belowRangeStartIsNotAcceptedAsValue() {
+ dateField.setRangeStart(date);
+ dateField.setValue(date.minusDays(1));
+ Assert.assertNotNull(dateField.getComponentError());
}
@Test
@@ -46,12 +51,16 @@ public class DateFieldTestCase {
}
@Test
- public void rangeEndIsImmutable() {
- long expectedTime = date.getTime();
-
+ public void rangeEndIsAcceptedAsValue() {
dateField.setRangeEnd(date);
- date.setTime(expectedTime + 1);
+ dateField.setValue(date);
+ Assert.assertNull(dateField.getComponentError());
+ }
- assertThat(dateField.getRangeEnd().getTime(), is(expectedTime));
+ @Test
+ public void aboveRangeEndIsNotAcceptedAsValue() {
+ dateField.setRangeEnd(date);
+ dateField.setValue(date.plusDays(1));
+ Assert.assertNotNull(dateField.getComponentError());
}
}