001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.extensions.markup.html.form.datetime;
018
019import java.time.LocalDate;
020import java.time.chrono.IsoChronology;
021import java.time.format.DateTimeFormatter;
022import java.time.format.DateTimeFormatterBuilder;
023import java.time.format.DateTimeParseException;
024import java.time.format.FormatStyle;
025import java.time.temporal.TemporalAccessor;
026import java.util.Locale;
027
028import org.apache.wicket.markup.html.form.AbstractTextComponent.ITextFormatProvider;
029import org.apache.wicket.markup.html.form.TextField;
030import org.apache.wicket.model.IModel;
031import org.apache.wicket.util.convert.IConverter;
032import org.apache.wicket.util.convert.converter.LocalDateConverter;
033import org.apache.wicket.util.string.Strings;
034
035/**
036 * A TextField that is mapped to a <code>java.time.LocalDate</code> object and that uses java.time time to
037 * parse and format values.
038 * 
039 * @see java.time.format.DateTimeFormatter
040 * 
041 * @author eelcohillenius
042 */
043public class LocalDateTextField extends TextField<LocalDate> implements ITextFormatProvider
044{
045        private static final long serialVersionUID = 1L;
046
047        /**
048         * The converter for the TextField
049         */
050        private final TextFormatConverter converter;
051        
052        /**
053         * Construct with a pattern.
054         * 
055         * @param id
056         *            the component id
057         * @param pattern
058         *            the pattern to use
059         */
060        public LocalDateTextField(String id, String pattern)
061        {
062                this(id, null, pattern);
063        }
064        
065        /**
066         * Construct with a pattern.
067         * 
068         * @param id
069         *            the component id
070         * @param model
071         *            the model
072         * @param pattern
073         *            the pattern to use
074         */
075        public LocalDateTextField(String id, IModel<LocalDate> model, String pattern)
076        {
077                this(id, model, pattern, pattern);
078        }
079
080        /**
081         * Construct with pattern for formatting and parsing.
082         * 
083         * @param id
084         *            the component id
085         * @param model
086         *            the model
087         * @param formatPattern
088         *            the pattern to use for formatting
089         * @param parsePattern
090         *            the pattern to use for parsing
091         */
092        public LocalDateTextField(String id, IModel<LocalDate> model, String formatPattern, String parsePattern)
093        {
094                super(id, model, LocalDate.class);
095
096                this.converter = new TextFormatConverter() {
097                        private static final long serialVersionUID = 1L;
098
099                        @Override
100                        protected DateTimeFormatter getDateTimeFormatter()
101                        {
102                                return DateTimeFormatter.ofPattern(formatPattern);
103                        }
104
105                        /**
106                         * Overwritten to support a custom parse pattern.
107                         */
108                        @Override
109                        public LocalDate convertToObject(final String value, Locale locale)
110                        {
111                                if (Strings.isEmpty(value))
112                                {
113                                        return null;
114                                }
115
116                                DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(parsePattern).withLocale(locale);
117                                TemporalAccessor temporalAccessor;
118                                try {
119                                        temporalAccessor = dateTimeFormatter.parse(value);
120                                } catch (DateTimeParseException ex) {
121                                        throw newConversionException("Cannot parse '" + value, value, locale);
122                                }
123                                
124                                return createTemporal(temporalAccessor);
125                        }
126
127                        @Override
128                        public String getTextFormat(Locale locale)
129                        {
130                                return formatPattern;
131                        }
132                };
133        }
134
135        /**
136         * Construct with a style.
137         * 
138         * @param id
139         *            the component id
140         * @param dateStyle
141         *            the style to use
142         */
143        public LocalDateTextField(String id, FormatStyle dateStyle)
144        {
145                this(id, null, dateStyle);
146        }
147
148        /**
149         * Construct with a style.
150         * 
151         * @param id
152         *            the component id
153         * @param model
154         *            the model
155         * @param dateStyle
156         *            the style to use
157         */
158        public LocalDateTextField(String id, IModel<LocalDate> model, FormatStyle dateStyle)
159        {
160                super(id, model, LocalDate.class);
161
162                this.converter = new TextFormatConverter() {
163                        private static final long serialVersionUID = 1L;
164
165                        @Override
166                        protected DateTimeFormatter getDateTimeFormatter()
167                        {
168                                return DateTimeFormatter.ofLocalizedDate(dateStyle);
169                        }
170
171                        @Override
172                        public String getTextFormat(Locale locale)
173                        {
174                                return DateTimeFormatterBuilder.getLocalizedDateTimePattern(dateStyle, null, IsoChronology.INSTANCE, locale);
175                        }
176                };
177        }
178
179        /**
180         * @return The specialized converter.
181         * @see org.apache.wicket.Component#createConverter(java.lang.Class)
182         */
183        @Override
184        protected IConverter<?> createConverter(Class<?> clazz)
185        {
186                if (LocalDate.class.isAssignableFrom(clazz))
187                {
188                        return converter;
189                }
190                return null;
191        }
192
193        /**
194         * @see org.apache.wicket.markup.html.form.AbstractTextComponent.ITextFormatProvider#getTextFormat()
195         */
196        @Override
197        public final String getTextFormat()
198        {
199                return converter.getTextFormat(getLocale());
200        }
201
202        private abstract class TextFormatConverter extends LocalDateConverter {
203                private static final long serialVersionUID = 1L;
204
205                public abstract String getTextFormat(Locale locale);
206        }
207}