diff options
author | Denis <denis@vaadin.com> | 2017-01-19 10:01:03 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-01-19 10:01:03 +0200 |
commit | f42b9657b818da483b839d3b43b4cf55552ef034 (patch) | |
tree | dd65e611eb326469b4dbeb84eefc2c69b57f3e5f /server/src/main/java | |
parent | dafc8310259a2e79bb203c7f786c9aba5354937b (diff) | |
download | vaadin-framework-f42b9657b818da483b839d3b43b4cf55552ef034.tar.gz vaadin-framework-f42b9657b818da483b839d3b43b4cf55552ef034.zip |
Introduce DateTimeFile and InlineDateTimeField. (#8218)
* Introduce DateTimeFile and InlineDateTimeField.
Fixes #8132
* Correct and provide declarative tests.
* Provide a date converter and UI tests.
Diffstat (limited to 'server/src/main/java')
8 files changed, 524 insertions, 5 deletions
diff --git a/server/src/main/java/com/vaadin/data/converter/LocalDateTimeToDateConverter.java b/server/src/main/java/com/vaadin/data/converter/LocalDateTimeToDateConverter.java new file mode 100644 index 0000000000..e427d956e3 --- /dev/null +++ b/server/src/main/java/com/vaadin/data/converter/LocalDateTimeToDateConverter.java @@ -0,0 +1,74 @@ +/* + * 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.data.converter; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Date; +import java.util.Objects; + +import com.vaadin.data.Converter; +import com.vaadin.data.Result; +import com.vaadin.data.ValueContext; +import com.vaadin.ui.DateTimeField; +import com.vaadin.ui.InlineDateTimeField; + +/** + * A converter that converts between <code>LocalDateTime</code> and + * <code>Date</code>. This is used when a {@link DateTimeField} or + * {@link InlineDateTimeField} is bound to a {@link Date} property. + * + * @author Vaadin Ltd + */ +public class LocalDateTimeToDateConverter + implements Converter<LocalDateTime, Date> { + + private ZoneOffset zoneOffset; + + /** + * Creates a new converter using the given time zone. + * + * @param zoneOffset + * the time zone offset to use, not <code>null</code> + */ + public LocalDateTimeToDateConverter(ZoneOffset zoneOffset) { + this.zoneOffset = Objects.requireNonNull(zoneOffset, + "Zone offset cannot be null"); + } + + @Override + public Result<Date> convertToModel(LocalDateTime localDate, + ValueContext context) { + if (localDate == null) { + return Result.ok(null); + } + + return Result.ok(Date.from(localDate.toInstant(zoneOffset))); + } + + @Override + public LocalDateTime convertToPresentation(Date date, + ValueContext context) { + if (date == null) { + return null; + } + + return Instant.ofEpochMilli(date.getTime()).atZone(zoneOffset) + .toLocalDateTime(); + } + +} diff --git a/server/src/main/java/com/vaadin/data/validator/DateTimeRangeValidator.java b/server/src/main/java/com/vaadin/data/validator/DateTimeRangeValidator.java new file mode 100644 index 0000000000..9096a8cc70 --- /dev/null +++ b/server/src/main/java/com/vaadin/data/validator/DateTimeRangeValidator.java @@ -0,0 +1,51 @@ +/* + * 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.data.validator; + +import java.time.LocalDateTime; +import java.util.Comparator; + +/** + * Validator for validating that a {@link LocalDateTime} is inside a given + * range. + * + * @author Vaadin Ltd. + * @since 8.0 + */ +public class DateTimeRangeValidator extends RangeValidator<LocalDateTime> { + + /** + * Creates a validator for checking that a {@link LocalDateTime} is within a + * given range. + * <p> + * By default the range is inclusive i.e. both minValue and maxValue are + * valid values. Use {@link #setMinValueIncluded(boolean)} or + * {@link #setMaxValueIncluded(boolean)} to change it. + * </p> + * + * @param errorMessage + * the message to display in case the value does not validate. + * @param minValue + * The minimum value to accept or null for no limit + * @param maxValue + * The maximum value to accept or null for no limit + */ + public DateTimeRangeValidator(String errorMessage, LocalDateTime minValue, + LocalDateTime maxValue) { + super(errorMessage, Comparator.naturalOrder(), minValue, maxValue); + } + +} diff --git a/server/src/main/java/com/vaadin/ui/AbstractLocalDateField.java b/server/src/main/java/com/vaadin/ui/AbstractLocalDateField.java index ab99fe0f28..7754757429 100644 --- a/server/src/main/java/com/vaadin/ui/AbstractLocalDateField.java +++ b/server/src/main/java/com/vaadin/ui/AbstractLocalDateField.java @@ -27,7 +27,11 @@ import com.vaadin.shared.ui.datefield.AbstractTextualDateFieldState; import com.vaadin.shared.ui.datefield.DateResolution; /** + * Abstract DateField class for {@link LocalDate} type. + * * @author Vaadin Ltd + * + * @since 8.0 * */ public abstract class AbstractLocalDateField diff --git a/server/src/main/java/com/vaadin/ui/AbstractLocalDateTimeField.java b/server/src/main/java/com/vaadin/ui/AbstractLocalDateTimeField.java index 9be77444c6..65da2a2b8b 100644 --- a/server/src/main/java/com/vaadin/ui/AbstractLocalDateTimeField.java +++ b/server/src/main/java/com/vaadin/ui/AbstractLocalDateTimeField.java @@ -15,14 +15,24 @@ */ package com.vaadin.ui; +import java.time.Instant; import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.temporal.ChronoUnit; +import java.util.Date; +import java.util.Map; +import com.vaadin.data.validator.DateTimeRangeValidator; +import com.vaadin.data.validator.RangeValidator; +import com.vaadin.shared.ui.datefield.AbstractTextualDateFieldState; import com.vaadin.shared.ui.datefield.DateTimeResolution; -import com.vaadin.shared.ui.datefield.LocalDateTimeFieldState; /** + * Abstract DateField class for {@link LocalDateTime} type. + * * @author Vaadin Ltd * + * @since 8.0 */ public abstract class AbstractLocalDateTimeField extends AbstractDateField<LocalDateTime, DateTimeResolution> { @@ -45,14 +55,112 @@ public abstract class AbstractLocalDateTimeField super(caption, DateTimeResolution.MINUTE); } + /** + * Constructs a new <code>AbstractLocalDateTimeField</code> with the given + * caption and initial text contents. + * + * @param caption + * the caption <code>String</code> for the editor. + * @param value + * the LocalDateTime value. + */ + public AbstractLocalDateTimeField(String caption, LocalDateTime value) { + super(caption, value, DateTimeResolution.MINUTE); + } + + @Override + protected AbstractTextualDateFieldState getState() { + return (AbstractTextualDateFieldState) super.getState(); + } + + @Override + protected AbstractTextualDateFieldState getState(boolean markAsDirty) { + return (AbstractTextualDateFieldState) super.getState(markAsDirty); + } + @Override - protected LocalDateTimeFieldState getState() { - return (LocalDateTimeFieldState) super.getState(); + protected int getDatePart(LocalDateTime date, + DateTimeResolution resolution) { + LocalDateTime value = date; + if (value == null) { + value = LocalDateTime.of(1, 1, 1, 0, 0); + } + switch (resolution) { + case DAY: + return value.getDayOfMonth(); + case MONTH: + return value.getMonthValue(); + case YEAR: + return value.getYear(); + case HOUR: + return value.getHour(); + case MINUTE: + return value.getMinute(); + case SECOND: + return value.getSecond(); + default: + assert false : "Unexpected resolution argument " + resolution; + return -1; + } } @Override - protected LocalDateTimeFieldState getState(boolean markAsDirty) { - return (LocalDateTimeFieldState) super.getState(markAsDirty); + protected RangeValidator<LocalDateTime> getRangeValidator() { + return new DateTimeRangeValidator(getDateOutOfRangeMessage(), + getDate(getRangeStart(), getResolution()), + getDate(getRangeEnd(), getResolution())); + } + + @Override + protected LocalDateTime buildDate( + Map<DateTimeResolution, Integer> resolutionValues) { + return LocalDateTime.of(resolutionValues.get(DateTimeResolution.YEAR), + resolutionValues.getOrDefault(DateTimeResolution.MONTH, 1), + resolutionValues.getOrDefault(DateTimeResolution.DAY, 1), + resolutionValues.getOrDefault(DateTimeResolution.HOUR, 0), + resolutionValues.getOrDefault(DateTimeResolution.MINUTE, 0), + resolutionValues.getOrDefault(DateTimeResolution.SECOND, 0)); + } + + @Override + protected LocalDateTime convertFromDate(Date date) { + if (date == null) { + return null; + } + return Instant.ofEpochMilli(date.getTime()).atZone(ZoneOffset.UTC) + .toLocalDateTime(); + } + + @Override + protected Date convertToDate(LocalDateTime date) { + if (date == null) { + return null; + } + return Date.from(date.toInstant(ZoneOffset.UTC)); + } + + private LocalDateTime getDate(LocalDateTime date, + DateTimeResolution forResolution) { + if (date == null) { + return null; + } + switch (forResolution) { + case YEAR: + return date.withDayOfYear(1).toLocalDate().atStartOfDay(); + case MONTH: + return date.withDayOfMonth(1).toLocalDate().atStartOfDay(); + case DAY: + return date.toLocalDate().atStartOfDay(); + case HOUR: + return date.truncatedTo(ChronoUnit.HOURS); + case MINUTE: + return date.truncatedTo(ChronoUnit.MINUTES); + case SECOND: + return date.truncatedTo(ChronoUnit.SECONDS); + default: + assert false : "Unexpected resolution argument " + forResolution; + return null; + } } } diff --git a/server/src/main/java/com/vaadin/ui/DateTimeField.java b/server/src/main/java/com/vaadin/ui/DateTimeField.java new file mode 100644 index 0000000000..cde5633846 --- /dev/null +++ b/server/src/main/java/com/vaadin/ui/DateTimeField.java @@ -0,0 +1,139 @@ +/* + * 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; + +import java.time.LocalDateTime; + +import com.vaadin.shared.ui.datefield.LocalDateTimeFieldState; + +/** + * A date time entry component, which displays the actual date selector as a + * popup. + * + * @see AbstractLocalDateTimeField + * @see InlineDateTimeField + * @author Vaadin Ltd. + * @since 8.0 + */ +public class DateTimeField extends AbstractLocalDateTimeField { + + /** + * Constructs an empty <code>DateTimeField</code> with no caption. + */ + public DateTimeField() { + super(); + } + + /** + * Constructs a new <code>DateTimeField</code> with the given caption and + * initial text contents. + * + * @param caption + * the caption <code>String</code> for the editor. + * @param value + * the LocalDateTime value. + */ + public DateTimeField(String caption, LocalDateTime value) { + super(caption, value); + } + + /** + * Constructs an empty <code>DateTimeField</code> with caption. + * + * @param caption + * the caption of the datefield. + */ + public DateTimeField(String caption) { + super(caption); + } + + /** + * Returns the current placeholder text. + * + * @see #setPlaceholder(String) + * @return the placeholder text + */ + public String getPlaceholder() { + return getState(false).placeholder; + } + + /** + * Sets the placeholder text. The placeholder is text that is displayed when + * the field would otherwise be empty, to prompt the user for input. + * + * @param placeholder + * the placeholder text to set + */ + public void setPlaceholder(String placeholder) { + getState().placeholder = placeholder; + } + + @Override + protected LocalDateTimeFieldState getState() { + return (LocalDateTimeFieldState) super.getState(); + } + + @Override + protected LocalDateTimeFieldState getState(boolean markAsDirty) { + return (LocalDateTimeFieldState) super.getState(markAsDirty); + } + + /** + * Checks whether the text field is enabled (default) or not. + * + * @see #setTextFieldEnabled(boolean) + * + * @return <b>true</b> if the text field is enabled, <b>false</b> otherwise. + */ + public boolean isTextFieldEnabled() { + return getState(false).textFieldEnabled; + } + + /** + * Enables or disables the text field. By default the text field is enabled. + * Disabling it causes only the button for date selection to be active, thus + * preventing the user from entering invalid dates. + * + * See {@link http://dev.vaadin.com/ticket/6790}. + * + * @param state + * <b>true</b> to enable text field, <b>false</b> to disable it. + */ + public void setTextFieldEnabled(boolean state) { + getState().textFieldEnabled = state; + } + + /** + * Set a description that explains the usage of the Widget for users of + * assistive devices. + * + * @param description + * String with the description + */ + public void setAssistiveText(String description) { + getState().descriptionForAssistiveDevices = description; + } + + /** + * Get the description that explains the usage of the Widget for users of + * assistive devices. + * + * @return String with the description + */ + public String getAssistiveText() { + return getState(false).descriptionForAssistiveDevices; + } +} diff --git a/server/src/main/java/com/vaadin/ui/InlineDateTimeField.java b/server/src/main/java/com/vaadin/ui/InlineDateTimeField.java new file mode 100644 index 0000000000..2c4cf290b3 --- /dev/null +++ b/server/src/main/java/com/vaadin/ui/InlineDateTimeField.java @@ -0,0 +1,71 @@ +/* + * 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; + +import java.time.LocalDateTime; + +import com.vaadin.shared.ui.datefield.InlineDateTimeFieldState; + +/** + * A date time entry component, which displays the actual date selector inline. + * + * @see AbstractLocalDateTimeField + * @see DateTimeField + * @author Vaadin Ltd. + * @since 8.0 + */ +public class InlineDateTimeField extends AbstractLocalDateTimeField { + + /** + * Constructs an empty <code>InlineDateTimeField</code> with no caption. + */ + public InlineDateTimeField() { + super(); + } + + /** + * Constructs a new <code>InlineDateTimeField</code> with the given caption + * and initial text contents. + * + * @param caption + * the caption <code>String</code> for the editor. + * @param value + * the LocalDate value. + */ + public InlineDateTimeField(String caption, LocalDateTime value) { + super(caption, value); + } + + /** + * Constructs an empty <code>InlineDateTimeField</code> with caption. + * + * @param caption + * the caption of the datefield. + */ + public InlineDateTimeField(String caption) { + super(caption); + } + + @Override + protected InlineDateTimeFieldState getState() { + return (InlineDateTimeFieldState) super.getState(); + } + + @Override + protected InlineDateTimeFieldState getState(boolean markAsDirty) { + return (InlineDateTimeFieldState) super.getState(markAsDirty); + } +} 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 6e9ff8f9bb..0f40be795b 100644 --- a/server/src/main/java/com/vaadin/ui/declarative/DesignFormatter.java +++ b/server/src/main/java/com/vaadin/ui/declarative/DesignFormatter.java @@ -21,6 +21,7 @@ import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.Collections; import java.util.Date; import java.util.Locale; @@ -42,6 +43,7 @@ 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.DesignLocalDateTimeConverter; import com.vaadin.ui.declarative.converters.DesignObjectConverter; import com.vaadin.ui.declarative.converters.DesignResourceConverter; import com.vaadin.ui.declarative.converters.DesignShortcutActionConverter; @@ -178,6 +180,8 @@ public class DesignFormatter implements Serializable { converterMap.put(Date.class, new DesignDateConverter()); converterMap.put(LocalDate.class, new DesignLocalDateConverter()); + converterMap.put(LocalDateTime.class, + new DesignLocalDateTimeConverter()); converterMap.put(ShortcutAction.class, new DesignShortcutActionConverter()); converterMap.put(Resource.class, new DesignResourceConverter()); diff --git a/server/src/main/java/com/vaadin/ui/declarative/converters/DesignLocalDateTimeConverter.java b/server/src/main/java/com/vaadin/ui/declarative/converters/DesignLocalDateTimeConverter.java new file mode 100644 index 0000000000..bfc177ca0c --- /dev/null +++ b/server/src/main/java/com/vaadin/ui/declarative/converters/DesignLocalDateTimeConverter.java @@ -0,0 +1,68 @@ +/* + * 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.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.Locale; + +import com.vaadin.data.Converter; +import com.vaadin.data.Result; +import com.vaadin.data.ValueContext; +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 DesignLocalDateTimeConverter + implements Converter<String, LocalDateTime> { + + @Override + public Result<LocalDateTime> convertToModel(String value, + ValueContext context) { + for (String pattern : new String[] { "yyyy-MM-dd HH:mm:ssZ", + "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM-dd HH", + "yyyy-MM-dd", "yyyy-MM", "yyyy" }) { + try { + Locale effectiveLocale = context.getLocale() + .orElse(Locale.ENGLISH); + LocalDateTime date = DateTimeFormatter + .ofPattern(pattern, effectiveLocale) + .parse(value, LocalDateTime::from); + return Result.ok(date); + } catch (DateTimeParseException ignored) { + // not parseable, ignore and try another format + } + } + return Result.error("Could not parse date value: " + value); + } + + @Override + public String convertToPresentation(LocalDateTime value, + ValueContext context) { + return DateTimeFormatter + .ofPattern("yyyy-MM-dd HH:mm:ss", + context.getLocale().orElse(Locale.ENGLISH)) + .format(value); + } + +} |