    001// Licensed under the Apache License, Version 2.0 (the "License");
    002// you may not use this file except in compliance with the License.
    003// You may obtain a copy of the License at
    004//
    005// http://www.apache.org/licenses/LICENSE-2.0
    006//
    007// Unless required by applicable law or agreed to in writing, software
    008// distributed under the License is distributed on an "AS IS" BASIS,
    009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    010// See the License for the specific language governing permissions and
    011// limitations under the License.
    012
    013package org.apache.tapestry5.corelib.components;
    014
    015import java.text.DateFormat;
    016import java.text.ParseException;
    017import java.text.SimpleDateFormat;
    018import java.util.Date;
    019
    020import org.apache.tapestry5.Binding;
    021import org.apache.tapestry5.BindingConstants;
    022import org.apache.tapestry5.ComponentResources;
    023import org.apache.tapestry5.EventConstants;
    024import org.apache.tapestry5.FieldValidator;
    025import org.apache.tapestry5.MarkupWriter;
    026import org.apache.tapestry5.ValidationException;
    027import org.apache.tapestry5.annotations.Events;
    028import org.apache.tapestry5.annotations.Parameter;
    029import org.apache.tapestry5.corelib.base.AbstractField;
    030import org.apache.tapestry5.ioc.Messages;
    031import org.apache.tapestry5.ioc.internal.util.InternalUtils;
    032import org.apache.tapestry5.services.ComponentDefaultProvider;
    033
    034/**
    035 * A component used to collect a provided date from the user using the native HTML5 date picker
    036 * (&lt;input type="date"&gt;)
    037 * @tapestrydoc
    038 * @see Form
    039 * @see TextField
    040 */
    041@Events(EventConstants.VALIDATE)
    042public class Html5DateField extends AbstractField
    043{
    044
    045    final private static String DATE_FORMAT = "yyyy-MM-dd";
    046
    047    /**
    048     * The value parameter of a DateField must be a {@link java.util.Date}.
    049     */
    050    @Parameter(required = true, principal = true, autoconnect = true)
    051    private Date value;
    052
    053    /**
    054     * The object that will perform input validation (which occurs after translation). The translate binding prefix is
    055     * generally used to provide this object in a declarative fashion.
    056     */
    057    @Parameter(defaultPrefix = BindingConstants.VALIDATE)
    058    private FieldValidator<Object> validate;
    059
    060    /**
    061     * Used to override the component's message catalog.
    062     *
    063     * @since 5.2.0.0
    064     * @deprecated Since 5.4; override the global message key "core-date-value-not-parsable" instead (see {@link org.apache.tapestry5.services.messages.ComponentMessagesSource})
    065     */
    066    @Parameter("componentResources.messages")
    067    private Messages messages;
    068
    069    /**
    070     * Computes a default value for the "validate" parameter using {@link ComponentDefaultProvider}.
    071     */
    072    final Binding defaultValidate()
    073    {
    074        return defaultProvider.defaultValidatorBinding("value", resources);
    075    }
    076
    077    void beginRender(MarkupWriter writer)
    078    {
    079        String value = validationTracker.getInput(this);
    080
    081        if (value == null)
    082        {
    083            value = formatCurrentValue();
    084        }
    085
    086        String clientId = getClientId();
    087
    088        writer.element("input",
    089
    090                "type", "date",
    091
    092                "class", cssClass,
    093
    094                "name", getControlName(),
    095
    096                "id", clientId,
    097
    098                "value", value);
    099
    100        writeDisabled(writer);
    101
    102        putPropertyNameIntoBeanValidationContext("value");
    103
    104        validate.render(writer);
    105
    106        removePropertyNameFromBeanValidationContext();
    107
    108        resources.renderInformalParameters(writer);
    109
    110        decorateInsideField();
    111
    112        writer.end();   // input
    113
    114    }
    115
    116    private void writeDisabled(MarkupWriter writer)
    117    {
    118        if (isDisabled())
    119            writer.attributes("disabled", "disabled");
    120    }
    121
    122    private String formatCurrentValue()
    123    {
    124        if (value == null)
    125            return "";
    126
    127        return getDateFormat().format(value);
    128    }
    129
    130    private DateFormat getDateFormat() {
    131        return new SimpleDateFormat(DATE_FORMAT);
    132    }
    133
    134    @Override
    135    protected void processSubmission(String controlName)
    136    {
    137        String value = request.getParameter(controlName);
    138
    139        validationTracker.recordInput(this, value);
    140
    141        Date parsedValue = null;
    142
    143        try
    144        {
    145            if (InternalUtils.isNonBlank(value))
    146                parsedValue = getDateFormat().parse(value);
    147        } catch (ParseException ex)
    148        {
    149            validationTracker.recordError(this, messages.format("core-date-value-not-parseable", value));
    150            return;
    151        }
    152
    153        putPropertyNameIntoBeanValidationContext("value");
    154        try
    155        {
    156            fieldValidationSupport.validate(parsedValue, resources, validate);
    157
    158            this.value = parsedValue;
    159        } catch (ValidationException ex)
    160        {
    161            validationTracker.recordError(this, ex.getMessage());
    162        }
    163
    164        removePropertyNameFromBeanValidationContext();
    165    }
    166
    167    void injectResources(ComponentResources resources)
    168    {
    169        this.resources = resources;
    170    }
    171
    172    @Override
    173    public boolean isRequired()
    174    {
    175        return validate.isRequired();
    176    }
    177}