001package org.unix4j.util.sort;
002
003import org.unix4j.util.StringUtil;
004
005import java.text.DateFormatSymbols;
006import java.util.Comparator;
007import java.util.Locale;
008
009/**
010 * A comparator Months: (unknown) < 'JAN' < ... < 'DEC'. The current locale
011 * determines the month spellings.
012 */
013public class MonthStringComparator implements Comparator<CharSequence> {
014
015        /**
016         * The instance for the default locale returned by {@link #getInstance()}.
017         */
018        private static final MonthStringComparator DEFAULT_INSTANCE = new MonthStringComparator();
019
020        /**
021         * Returns the instance for the default locale.
022         *
023         * @see Locale#getDefault()
024         */
025        public static MonthStringComparator getInstance() {
026                return DEFAULT_INSTANCE;
027        }
028
029        /**
030         * Returns an instance for the specified locale.
031         */
032        public static MonthStringComparator getInstance(Locale locale) {
033                return new MonthStringComparator(locale);
034        }
035
036        private final String[] months;
037        private final String[] shortMonths;
038
039        /**
040         * Private constructor used to create the {@link #DEFAULT_INSTANCE}.
041         */
042        private MonthStringComparator() {
043                this(DateFormatSymbols.getInstance());
044        }
045
046        /**
047         * Private constructor used by {@link #getInstance(Locale)}.
048         */
049        private MonthStringComparator(Locale locale) {
050                this(DateFormatSymbols.getInstance(locale));
051        }
052
053        /**
054         * Constructor with date symbols.
055         *
056         * @param symbols
057         *            the date symbols
058         */
059        public MonthStringComparator(DateFormatSymbols symbols) {
060                this.months = symbols.getMonths();
061                this.shortMonths = symbols.getShortMonths();
062        }
063
064        @Override
065        public int compare(CharSequence s1, CharSequence s2) {
066                final int start1 = StringUtil.findStartTrimWhitespace(s1);
067                final int end1 = StringUtil.findEndTrimWhitespace(s1);
068                final int start2 = StringUtil.findStartTrimWhitespace(s2);
069                final int end2 = StringUtil.findEndTrimWhitespace(s2);
070                final int month1 = month(s1, start1, end1);
071                final int month2 = month(s2, start2, end2);
072                return Integer.compare(month1, month2);
073        }
074
075        private final int month(CharSequence s, int start, int end) {
076                int m = month(months, s, start, end);
077                return m >= 0 ? m : month(shortMonths, s, start, end);
078        }
079
080        private static int month(String[] months, CharSequence s, int start, int end) {
081                for (int i = 0; i < months.length; i++) {
082                        final String month = months[i];
083                        if (!equalsIgnoreCase(month, s, start, end)) {
084                                continue;
085                        }
086                        return i;
087                }
088                return -1;
089        }
090
091        private static final boolean equalsIgnoreCase(String s1, CharSequence s2, int start, int end) {
092                if (end - start != s1.length()) {
093                        return false;
094                }
095                for (int i = start; i < end; i++) {
096                        if (!equalsIgnoreCase(s1.charAt(i - start), s2.charAt(i))) {
097                                return false;
098                        }
099                }
100                return true;
101        }
102        private static final boolean equalsIgnoreCase(char ch1, char ch2) {
103                return Character.toUpperCase(ch1) == Character.toUpperCase(ch2);
104        }
105}