001/*
002 * (C) Copyright 2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Olivier Grisel
018 */
019package org.nuxeo.ecm.platform.suggestbox.utils;
020
021import java.util.Calendar;
022import java.util.regex.Matcher;
023import java.util.regex.Pattern;
024
025import org.joda.time.Chronology;
026import org.joda.time.DateTime;
027import org.joda.time.IllegalFieldValueException;
028import org.joda.time.chrono.GregorianChronology;
029
030public class DateMatcher {
031
032    private static final Pattern YEAR_ONLY_MATCHER = Pattern.compile("^\\d{4}$");
033
034    private static final Pattern MONTH_DIGIT_ONLY_MATCHER = Pattern.compile("^\\d{2}$");
035
036    private static final Pattern YEAR_MONTHS_MATCHER = Pattern.compile("^\\d{4}[_ -:]\\d{2}$");
037
038    private static final Pattern MONTHS_YEAR_MATCHER = Pattern.compile("^\\d{2}[_ -:]\\d{4}$");
039
040    private static final Pattern MONTHS_DAY_YEAR_MATCHER = Pattern.compile("^\\d{2}[_ -:]\\d{2,}[_ -:]\\d{4}$");
041
042    private static final Pattern YEAR_MONTHS_DAY_MATCHER = Pattern.compile("^\\d{4}[_ -:]\\d{2,}[_ -:]\\d{2}$");
043
044    private boolean withYears = false;
045
046    private boolean withMonth = false;
047
048    private boolean withDay = false;
049
050    private final Calendar dateSuggestion;
051
052    private DateMatcher(boolean withYears, boolean withMonth, boolean withDay, Calendar dateSuggestion) {
053        this.withYears = withYears;
054        this.withMonth = withMonth;
055        this.withDay = withDay;
056        this.dateSuggestion = dateSuggestion;
057    }
058
059    public boolean isWithYears() {
060        return withYears;
061    }
062
063    public void setWithYears(boolean withYears) {
064        this.withYears = withYears;
065    }
066
067    public boolean isWithMonth() {
068        return withMonth;
069    }
070
071    public void setWithMonth(boolean withMonth) {
072        this.withMonth = withMonth;
073    }
074
075    public boolean isWitDay() {
076        return withDay;
077    }
078
079    public void setWitDay(boolean witDay) {
080        withDay = witDay;
081    }
082
083    public Calendar getDateSuggestion() {
084        return dateSuggestion;
085    }
086
087    public boolean hasMatch() {
088        return getDateSuggestion() != null;
089    }
090
091    public static Matcher parsingDate(Pattern pattern, String input) {
092        return pattern.matcher(input.trim());
093    }
094
095    public static DateMatcher fromInput(String input) {
096        try {
097            return doFromInput(input);
098        } catch (NumberFormatException e) {
099            return new DateMatcher(false, false, false, null);
100        }
101    }
102
103    public static DateMatcher doFromInput(String input) {
104        Matcher matcher = parsingDate(YEAR_ONLY_MATCHER, input);
105
106        if (matcher.find()) {
107            return new DateMatcher(true, false, false, dateToInstance(Integer.parseInt(matcher.group()), 1, 1));
108        }
109        matcher = parsingDate(MONTH_DIGIT_ONLY_MATCHER, input);
110        if (matcher.find()) {
111            int month = Integer.parseInt(matcher.group());
112            if (month > 12 || month < 1) {
113                return new DateMatcher(false, true, false, null);
114            }
115            return new DateMatcher(false, true, false, dateToInstance(Calendar.getInstance().get(Calendar.YEAR), month,
116                    1));
117        }
118        matcher = parsingDate(YEAR_MONTHS_MATCHER, input);
119        if (matcher.find()) {
120            int month = Integer.parseInt(matcher.group().substring(5));
121            if (month > 12 || month < 1) {
122                return new DateMatcher(true, true, false, null);
123            }
124            int year = Integer.parseInt(matcher.group().substring(0, 4));
125
126            return new DateMatcher(true, true, false, dateToInstance(year, month, 1));
127        }
128        matcher = parsingDate(MONTHS_YEAR_MATCHER, input);
129        if (matcher.find()) {
130            int month = Integer.parseInt(matcher.group().substring(0, 2));
131            if (month > 12 || month < 1) {
132                return new DateMatcher(true, true, false, null);
133            }
134            int year = Integer.parseInt(matcher.group().substring(3));
135
136            return new DateMatcher(true, true, false, dateToInstance(year, month, 1));
137
138        }
139        matcher = parsingDate(MONTHS_DAY_YEAR_MATCHER, input);
140        if (matcher.find()) {
141            int first = Integer.parseInt(matcher.group().substring(0, 2));
142            int second = Integer.parseInt(matcher.group().substring(3, 5));
143            int year = Integer.parseInt(matcher.group().substring(6));
144            int control = first + second;
145            if (control < 2 || control > 12 + 31 || first < 1 || second < 1) {
146                return new DateMatcher(true, true, true, null);
147            } else if (control < 12 + 12 + 1) {
148                return new DateMatcher(true, true, true, dateToInstance(year, first, second));
149            }
150            int month = first;
151            int day = second;
152            if (first > second) {
153                month = second;
154                day = first;
155            }
156            Calendar dateToInstance = dateToInstance(year, month, day);
157            return new DateMatcher(true, true, true, dateToInstance);
158        }
159        matcher = parsingDate(YEAR_MONTHS_DAY_MATCHER, input);
160        if (matcher.find()) {
161            int year = Integer.parseInt(matcher.group().substring(0, 4));
162            int first = Integer.parseInt(matcher.group().substring(5, 7));
163            int second = Integer.parseInt(matcher.group().substring(8));
164            int control = first + second;
165            if (control < 2 || control > 12 + 31 || first < 1 || second < 1) {
166                return new DateMatcher(true, true, true, null);
167            } else if (control < 12 + 12 + 1) {
168                return new DateMatcher(true, true, true, dateToInstance(year, first, second));
169            }
170            int month = first;
171            int day = second;
172            if (first > second) {
173                month = second;
174                day = first;
175            }
176            Calendar dateToInstance = dateToInstance(year, month, day);
177            return new DateMatcher(true, true, true, dateToInstance);
178        }
179
180        return new DateMatcher(false, false, false, null);
181    }
182
183    protected static Calendar dateToInstance(int year, int month, int day) {
184        try {
185            Chronology chrono = GregorianChronology.getInstance();
186            DateTime dt = new DateTime(year, month, day, 12, 0, 0, 0, chrono);
187            return dt.toGregorianCalendar();
188        } catch (IllegalArgumentException e) {
189            return null;
190        }
191    }
192
193}