diff options
author | Denis Anisimov <denis@vaadin.com> | 2016-09-27 10:41:37 +0300 |
---|---|---|
committer | Denis Anisimov <denis@vaadin.com> | 2016-09-30 06:56:18 +0000 |
commit | d36d63fefa3ab9f4908f97772bcc6499e4f52532 (patch) | |
tree | d2189db8f44601925a96435dedec031459eeed57 /server/src | |
parent | db3a91c9b4d8d0451c8f1c5e875f4c6f6f50324a (diff) | |
download | vaadin-framework-d36d63fefa3ab9f4908f97772bcc6499e4f52532.tar.gz vaadin-framework-d36d63fefa3ab9f4908f97772bcc6499e4f52532.zip |
Make AbstractDateField based on LocalDate (#125).
Change-Id: I33a4a4f0f3437a8d1733031a131afbe844c12afb
Diffstat (limited to 'server/src')
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()); } } |