17935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/*
27935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ********************************************************************************
3f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert * Copyright (C) 2006-2015, Google, International Business Machines Corporation *
47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and others. All Rights Reserved.                                             *
57935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ********************************************************************************
67935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.text;
87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.ArrayList;
107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Arrays;
117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.BitSet;
127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Collection;
137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.EnumSet;
147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.HashSet;
157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Iterator;
167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.LinkedHashMap;
177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.LinkedHashSet;
187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.List;
197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Locale;
207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Map;
217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.MissingResourceException;
227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Set;
237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.TreeMap;
247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.TreeSet;
257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.ICUCache;
277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.ICUResourceBundle;
287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.PatternTokenizer;
297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.SimpleCache;
307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.Utility;
317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.Calendar;
327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.Freezable;
337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ICUCloneNotSupportedException;
347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ULocale;
357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ULocale.Category;
367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.UResourceBundle;
377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/**
397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This class provides flexible generation of date format patterns, like
407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * "yy-MM-dd". The user can build up the generator by adding successive
417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * patterns. Once that is done, a query can be made using a "skeleton", which is
427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * a pattern which just includes the desired fields and lengths. The generator
437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * will return the "best fit" pattern corresponding to that skeleton.
447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>
457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The main method people will use is getBestPattern(String skeleton), since
467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * normally this class is pre-built with data from a particular locale. However,
477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * generators can be built directly from other data as well.
487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.6
497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic class DateTimePatternGenerator implements Freezable<DateTimePatternGenerator>, Cloneable {
517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final boolean DEBUG = false;
527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // debugging flags
547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //static boolean SHOW_DISTANCE = false;
557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // TODO add hack to fix months for CJK, as per bug ticket 1099
567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Create empty generator, to be constructed with addPattern(...) etc.
597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static DateTimePatternGenerator getEmptyInstance() {
627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new DateTimePatternGenerator();
637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Only for use by subclasses
677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected DateTimePatternGenerator() {
707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct a flexible generator according to data for the default <code>FORMAT</code> locale.
747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Category#FORMAT
757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static DateTimePatternGenerator getInstance() {
787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getInstance(ULocale.getDefault(Category.FORMAT));
797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct a flexible generator according to data for a given locale.
837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param uLocale The locale to pass.
847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static DateTimePatternGenerator getInstance(ULocale uLocale) {
877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getFrozenInstance(uLocale).cloneAsThawed();
887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct a flexible generator according to data for a given locale.
927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param locale The JDK locale to pass.
937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @draft ICU 54
947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @provisional This API might change or be removed in a future release.
957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static DateTimePatternGenerator getInstance(Locale locale) {
977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getInstance(ULocale.forLocale(locale));
987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct a frozen instance of DateTimePatternGenerator for a
1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * given locale.  This method returns a cached frozen instance of
1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * DateTimePatternGenerator, so less expensive than the regular
1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * factory method.
1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param uLocale The locale to pass.
1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return A frozen DateTimePatternGenerator.
1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static DateTimePatternGenerator getFrozenInstance(ULocale uLocale) {
1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String localeKey = uLocale.toString();
1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DateTimePatternGenerator result = DTPNG_CACHE.get(localeKey);
1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (result != null) {
1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return result;
1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        result = new DateTimePatternGenerator();
1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PatternInfo returnInfo = new PatternInfo();
1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String shortTimePattern = null;
1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // first load with the ICU patterns
1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = DateFormat.FULL; i <= DateFormat.SHORT; ++i) {
1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateInstance(i, uLocale);
1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.addPattern(df.toPattern(), false, returnInfo);
1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            df = (SimpleDateFormat) DateFormat.getTimeInstance(i, uLocale);
1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.addPattern(df.toPattern(), false, returnInfo);
1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (i == DateFormat.SHORT) {
1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // keep this pattern to populate other time field
1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // combination patterns by hackTimes later in this method.
1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                shortTimePattern = df.toPattern();
1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // use hour style in SHORT time pattern as the default
1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // hour style for the locale
1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                FormatParser fp = new FormatParser();
1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                fp.set(shortTimePattern);
1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                List<Object> items = fp.getItems();
1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (int idx = 0; idx < items.size(); idx++) {
1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    Object item = items.get(idx);
1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (item instanceof VariableField) {
1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        VariableField fld = (VariableField)item;
1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (fld.getType() == HOUR) {
1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            result.defaultHourFormatChar = fld.toString().charAt(0);
1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            break;
1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, uLocale);
1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Get the correct calendar type
1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String calendarTypeToUse = uLocale.getKeywordValue("calendar");
1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( calendarTypeToUse == null ) {
1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String[] preferredCalendarTypes = Calendar.getKeywordValuesForLocale("calendar", uLocale, true);
1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            calendarTypeToUse = preferredCalendarTypes[0]; // the most preferred calendar
1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( calendarTypeToUse == null ) {
1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            calendarTypeToUse = "gregorian"; // fallback
1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Get data for that calendar
1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        try {
1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //      ICU4J getWithFallback does not work well when
1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //      1) A nested table is an alias to /LOCALE/...
1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //      2) getWithFallback is called multiple times for going down hierarchical resource path
1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //      #9987 resolved the issue of alias table when full path is specified in getWithFallback,
1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //      but there is no easy solution when the equivalent operation is done by multiple operations.
1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //      This issue is addressed in #9964.
1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ICUResourceBundle itemBundle = rb.getWithFallback("calendar/" + calendarTypeToUse + "/appendItems");
1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i=0; i<itemBundle.getSize(); ++i) {
1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ICUResourceBundle formatBundle = (ICUResourceBundle)itemBundle.get(i);
1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String formatName = itemBundle.get(i).getKey();
1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String value = formatBundle.getString();
1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.setAppendItemFormat(getAppendFormatNumber(formatName), value);
1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }catch(MissingResourceException e) {
1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // CLDR item names
1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        try {
1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ICUResourceBundle itemBundle = rb.getWithFallback("fields");
1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ICUResourceBundle fieldBundle, dnBundle;
1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i=0; i<TYPE_LIMIT; ++i) {
1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ( isCLDRFieldName(i) ) {
1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    fieldBundle = itemBundle.getWithFallback(CLDR_FIELD_NAME[i]);
1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    dnBundle = fieldBundle.getWithFallback("dn");
1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String value = dnBundle.getString();
1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //System.out.println("Field name:"+value);
1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    result.setAppendItemName(i, value);
1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }catch(MissingResourceException e) {
1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // set the AvailableFormat in CLDR
1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ICUResourceBundle availFormatsBundle = null;
1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        try {
1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //      ICU4J getWithFallback does not work well when
1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //      1) A nested table is an alias to /LOCALE/...
1997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //      2) getWithFallback is called multiple times for going down hierarchical resource path
2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //      #9987 resolved the issue of alias table when full path is specified in getWithFallback,
2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //      but there is no easy solution when the equivalent operation is done by multiple operations.
2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //      This issue is addressed in #9964.
2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            availFormatsBundle = rb.getWithFallback("calendar/" + calendarTypeToUse + "/availableFormats");
2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } catch (MissingResourceException e) {
2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // fall through
2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean override = true;
2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        while (availFormatsBundle != null) {
2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = 0; i < availFormatsBundle.getSize(); i++) {
2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String formatKey = availFormatsBundle.get(i).getKey();
2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (!result.isAvailableFormatSet(formatKey)) {
2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    result.setAvailableFormat(formatKey);
2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Add pattern with its associated skeleton. Override any duplicate derived from std patterns,
2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // but not a previous availableFormats entry:
2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String formatValue = availFormatsBundle.get(i).getString();
2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    result.addPatternWithSkeleton(formatValue, formatKey, override, returnInfo);
2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ICUResourceBundle pbundle = (ICUResourceBundle)availFormatsBundle.getParent();
2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (pbundle == null) {
2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            try {
2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                availFormatsBundle = pbundle.getWithFallback("calendar/" + calendarTypeToUse + "/availableFormats");
2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } catch (MissingResourceException e) {
2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                availFormatsBundle = null;
2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (availFormatsBundle != null && pbundle.getULocale().getBaseName().equals("root")) {
2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                override = false;
2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // assume it is always big endian (ok for CLDR right now)
2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // some languages didn't add mm:ss or HH:mm, so put in a hack to compute that from the short time.
2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (shortTimePattern != null) {
2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            hackTimes(result, returnInfo, shortTimePattern);
2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        result.setDateTimeFormat(Calendar.getDateTimePattern(Calendar.getInstance(uLocale), uLocale, DateFormat.MEDIUM));
2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // decimal point for seconds
2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DecimalFormatSymbols dfs = new DecimalFormatSymbols(uLocale);
2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        result.setDecimal(String.valueOf(dfs.getDecimalSeparator()));
2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // freeze and cache
2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        result.freeze();
2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DTPNG_CACHE.put(localeKey, result);
2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result;
2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
2577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public char getDefaultHourFormatChar() {
2607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return defaultHourFormatChar;
2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
2687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void setDefaultHourFormatChar(char defaultHourFormatChar) {
2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        this.defaultHourFormatChar = defaultHourFormatChar;
2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static void hackTimes(DateTimePatternGenerator result, PatternInfo returnInfo, String hackPattern) {
2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        result.fp.set(hackPattern);
2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder mmss = new StringBuilder();
2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // to get mm:ss, we strip all but mm literal ss
2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean gotMm = false;
2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < result.fp.items.size(); ++i) {
2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Object item = result.fp.items.get(i);
2797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (item instanceof String) {
2807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (gotMm) {
2817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    mmss.append(result.fp.quoteLiteral(item.toString()));
2827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                char ch = item.toString().charAt(0);
2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (ch == 'm') {
2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    gotMm = true;
2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    mmss.append(item);
2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (ch == 's') {
2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (!gotMm) {
2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        break; // failed
2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    mmss.append(item);
2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    result.addPattern(mmss.toString(), false, returnInfo);
2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    break;
2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (gotMm || ch == 'z' || ch == 'Z' || ch == 'v' || ch == 'V') {
2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    break; // failed
2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // to get hh:mm, we strip (literal ss) and (literal S)
3017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // the easiest way to do this is to mark the stuff we want to nuke, then remove it in a second pass.
3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        BitSet variables = new BitSet();
3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        BitSet nuke = new BitSet();
3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < result.fp.items.size(); ++i) {
3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Object item = result.fp.items.get(i);
3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (item instanceof VariableField) {
3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                variables.set(i);
3087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                char ch = item.toString().charAt(0);
3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (ch == 's' || ch == 'S') {
3107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    nuke.set(i);
3117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    for (int j = i-1; j >= 0; ++j) {
3127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (variables.get(j)) break;
3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        nuke.set(i);
3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
3157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
3167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String hhmm = getFilteredPattern(result.fp, nuke);
3197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        result.addPattern(hhmm, false, returnInfo);
3207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String getFilteredPattern(FormatParser fp, BitSet nuke) {
3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder result = new StringBuilder();
3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < fp.items.size(); ++i) {
3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (nuke.get(i)) continue;
3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Object item = fp.items.get(i);
3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (item instanceof String) {
3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.append(fp.quoteLiteral(item.toString()));
3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.append(item.toString());
3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result.toString();
3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*private static int getAppendNameNumber(String string) {
3377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < CLDR_FIELD_NAME.length; ++i) {
3387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (CLDR_FIELD_NAME[i].equals(string)) return i;
3397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return -1;
3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }*/
3427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal CLDR
3457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
3467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
3487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static int getAppendFormatNumber(String string) {
3497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < CLDR_FIELD_APPEND.length; ++i) {
3507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (CLDR_FIELD_APPEND[i].equals(string)) return i;
3517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return -1;
3537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static boolean isCLDRFieldName(int index) {
3577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ((index<0) && (index>=TYPE_LIMIT)) {
3587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return false;
3597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (CLDR_FIELD_NAME[index].charAt(0) == '*') {
3617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return false;
3627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        else {
3647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return true;
3657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Return the best pattern matching the input skeleton. It is guaranteed to
3707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * have all of the fields in the skeleton.
3717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>Example code:{@.jcite com.ibm.icu.samples.text.datetimepatterngenerator.DateTimePatternGeneratorSample:---getBestPatternExample}
3727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton The skeleton is a pattern containing only the variable fields.
3737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            For example, "MMMdd" and "mmhh" are skeletons.
3747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return Best pattern matching the input skeleton.
3757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
3767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getBestPattern(String skeleton) {
3787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getBestPattern(skeleton, null, MATCH_NO_OPTIONS);
3797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Return the best pattern matching the input skeleton. It is guaranteed to
3837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * have all of the fields in the skeleton.
3847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
3857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton The skeleton is a pattern containing only the variable fields.
3867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            For example, "MMMdd" and "mmhh" are skeletons.
3877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param options MATCH_xxx options for forcing the length of specified fields in
3887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            the returned pattern to match those in the skeleton (when this would
3897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            not happen otherwise). For default behavior, use MATCH_NO_OPTIONS.
3907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return Best pattern matching the input skeleton (and options).
3917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.4
3927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getBestPattern(String skeleton, int options) {
3947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getBestPattern(skeleton, null, options);
3957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
3987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * getBestPattern which takes optional skip matcher
3997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private String getBestPattern(String skeleton, DateTimeMatcher skipMatcher, int options) {
4017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        EnumSet<DTPGflags> flags = EnumSet.noneOf(DTPGflags.class);
4027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Replace hour metacharacters 'j' and 'J', set flags as necessary
4037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder skeletonCopy = new StringBuilder(skeleton);
4047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean inQuoted = false;
4057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int patPos = 0; patPos < skeletonCopy.length(); patPos++) {
4067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            char patChr = skeletonCopy.charAt(patPos);
4077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (patChr == '\'') {
4087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                inQuoted = !inQuoted;
4097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if (!inQuoted) {
4107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (patChr == 'j') {
4117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    skeletonCopy.setCharAt(patPos, defaultHourFormatChar);
4127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (patChr == 'J') {
4137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                	// Get pattern for skeleton with H, then (in adjustFieldTypes)
4147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                	// replace H or k with defaultHourFormatChar
4157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                	skeletonCopy.setCharAt(patPos, 'H');
4167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                	flags.add(DTPGflags.SKELETON_USES_CAP_J);
4177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
4187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String datePattern, timePattern;
4227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        synchronized(this) {
4237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            current.set(skeletonCopy.toString(), fp, false);
4247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            PatternWithMatcher bestWithMatcher = getBestRaw(current, -1, _distanceInfo, skipMatcher);
4257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (_distanceInfo.missingFieldMask == 0 && _distanceInfo.extraFieldMask == 0) {
4267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // we have a good item. Adjust the field types
4277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return adjustFieldTypes(bestWithMatcher, current, flags, options);
4287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int neededFields = current.getFieldMask();
4307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // otherwise break up by date and time.
4327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            datePattern = getBestAppending(current, neededFields & DATE_MASK, _distanceInfo, skipMatcher, flags, options);
4337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            timePattern = getBestAppending(current, neededFields & TIME_MASK, _distanceInfo, skipMatcher, flags, options);
4347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (datePattern == null) return timePattern == null ? "" : timePattern;
4377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (timePattern == null) return datePattern;
4387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return MessageFormat.format(getDateTimeFormat(), new Object[]{timePattern, datePattern});
4397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * PatternInfo supplies output parameters for addPattern(...). It is used because
4437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Java doesn't have real output parameters. It is treated like a struct (eg
4447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Point), so all fields are public.
4457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
4477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final class PatternInfo { // struct for return information
4497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 3.6
4517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public static final int OK = 0;
4537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 3.6
4567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public static final int BASE_CONFLICT = 1;
4587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 3.6
4617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public static final int CONFLICT = 2;
4637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 3.6
4667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int status;
4687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 3.6
4717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String conflictingPattern;
4737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Simple constructor, since this is treated like a struct.
4767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 3.6
4777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public PatternInfo() {
4797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Adds a pattern to the generator. If the pattern has the same skeleton as
4847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * an existing pattern, and the override parameter is set, then the previous
4857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * value is overridden. Otherwise, the previous value is retained. In either
4867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * case, the conflicting information is returned in PatternInfo.
4877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>
4887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Note that single-field patterns (like "MMM") are automatically added, and
4897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * don't need to be added explicitly!
4907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * * <p>Example code:{@.jcite com.ibm.icu.samples.text.datetimepatterngenerator.DateTimePatternGeneratorSample:---addPatternExample}
4917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param pattern Pattern to add.
4927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param override When existing values are to be overridden use true, otherwise
4937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            use false.
4947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param returnInfo Returned information.
4957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
4967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public DateTimePatternGenerator addPattern(String pattern, boolean override, PatternInfo returnInfo) {
4987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return addPatternWithSkeleton(pattern, null, override, returnInfo);
4997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * addPatternWithSkeleton:
5037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If skeletonToUse is specified, then an availableFormats entry is being added. In this case:
5047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 1. We pass that skeleton to DateTimeMatcher().set instead of having it derive a skeleton from the pattern.
5057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 2. If the new entry's skeleton or basePattern does match an existing entry but that entry also had a skeleton specified
5067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * (i.e. it was also from availableFormats), then the new entry does not override it regardless of the value of the override
5077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * parameter. This prevents later availableFormats entries from a parent locale overriding earlier ones from the actual
5087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * specified locale. However, availableFormats entries *should* override entries with matching skeleton whose skeleton was
5097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * derived (i.e. entries derived from the standard date/time patters for the specified locale).
5107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 3. When adding the pattern (skeleton2pattern.put, basePattern_pattern.put), we set a field to indicate that the added
5117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * entry had a specified skeleton.
5127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
5137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
5147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
5167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public DateTimePatternGenerator addPatternWithSkeleton(String pattern, String skeletonToUse, boolean override, PatternInfo returnInfo) {
5177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        checkFrozen();
5187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DateTimeMatcher matcher;
5197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (skeletonToUse == null) {
5207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            matcher = new DateTimeMatcher().set(pattern, fp, false);
5217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
5227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            matcher = new DateTimeMatcher().set(skeletonToUse, fp, false);
5237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String basePattern = matcher.getBasePattern();
5257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // We only care about base conflicts - and replacing the pattern associated with a base - if:
5267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // 1. the conflicting previous base pattern did *not* have an explicit skeleton; in that case the previous
5277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // base + pattern combination was derived from either (a) a canonical item, (b) a standard format, or
5287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // (c) a pattern specified programmatically with a previous call to addPattern (which would only happen
5297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // if we are getting here from a subsequent call to addPattern).
5307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // 2. a skeleton is specified for the current pattern, but override=false; in that case we are checking
5317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // availableFormats items from root, which should not override any previous entry with the same base.
5327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PatternWithSkeletonFlag previousPatternWithSameBase = basePattern_pattern.get(basePattern);
5337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (previousPatternWithSameBase != null && (!previousPatternWithSameBase.skeletonWasSpecified || (skeletonToUse != null && !override))) {
5347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            returnInfo.status = PatternInfo.BASE_CONFLICT;
5357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            returnInfo.conflictingPattern = previousPatternWithSameBase.pattern;
5367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!override) {
5377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return this;
5387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // The only time we get here with override=true and skeletonToUse!=null is when adding availableFormats
5417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // items from CLDR data. In that case, we don't want an item from a parent locale to replace an item with
5427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // same skeleton from the specified locale, so skip the current item if skeletonWasSpecified is true for
5437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // the previously-specified conflicting item.
5447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PatternWithSkeletonFlag previousValue = skeleton2pattern.get(matcher);
5457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (previousValue != null) {
5467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            returnInfo.status = PatternInfo.CONFLICT;
5477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            returnInfo.conflictingPattern = previousValue.pattern;
5487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!override || (skeletonToUse != null && previousValue.skeletonWasSpecified)) return this;
5497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        returnInfo.status = PatternInfo.OK;
5517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        returnInfo.conflictingPattern = "";
5527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PatternWithSkeletonFlag patWithSkelFlag = new PatternWithSkeletonFlag(pattern,skeletonToUse != null);
5537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (DEBUG) {
5547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            System.out.println(matcher + " => " + patWithSkelFlag);
5557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        skeleton2pattern.put(matcher, patWithSkelFlag);
5577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        basePattern_pattern.put(basePattern, patWithSkelFlag);
5587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return this;
5597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Utility to return a unique skeleton from a given pattern. For example,
5637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * both "MMM-dd" and "dd/MMM" produce the skeleton "MMMdd".
5647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param pattern Input pattern, such as "dd/MMM"
5667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return skeleton, such as "MMMdd"
5677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
5687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getSkeleton(String pattern) {
5707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        synchronized (this) { // synchronized since a getter must be thread-safe
5717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            current.set(pattern, fp, false);
5727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return current.toString();
5737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Same as getSkeleton, but allows duplicates
5787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param pattern Input pattern, such as "dd/MMM"
5807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return skeleton, such as "MMMdd"
5817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
5827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
5837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
5857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getSkeletonAllowingDuplicates(String pattern) {
5867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        synchronized (this) { // synchronized since a getter must be thread-safe
5877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            current.set(pattern, fp, true);
5887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return current.toString();
5897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Same as getSkeleton, but allows duplicates
5947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and returns a string using canonical pattern chars
5957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param pattern Input pattern, such as "ccc, d LLL"
5977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return skeleton, such as "MMMEd"
5987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
5997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
6007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
6017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
6027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getCanonicalSkeletonAllowingDuplicates(String pattern) {
6037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        synchronized (this) { // synchronized since a getter must be thread-safe
6047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            current.set(pattern, fp, true);
6057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return current.toCanonicalString();
6067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
6107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Utility to return a unique base skeleton from a given pattern. This is
6117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the same as the skeleton, except that differences in length are minimized
6127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * so as to only preserve the difference between string and numeric form. So
6137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * for example, both "MMM-dd" and "d/MMM" produce the skeleton "MMMd"
6147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * (notice the single d).
6157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
6167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param pattern Input pattern, such as "dd/MMM"
6177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return skeleton, such as "MMMdd"
6187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
6197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
6207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getBaseSkeleton(String pattern) {
6217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        synchronized (this) { // synchronized since a getter must be thread-safe
6227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            current.set(pattern, fp, false);
6237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return current.getBasePattern();
6247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
6287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Return a list of all the skeletons (in canonical form) from this class,
6297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and the patterns that they map to.
6307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
6317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param result an output Map in which to place the mapping from skeleton to
6327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            pattern. If you want to see the internal order being used,
6337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            supply a LinkedHashMap. If the input value is null, then a
6347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            LinkedHashMap is allocated.
6357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            <p>
6367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            <i>Issue: an alternate API would be to just return a list of
6377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            the skeletons, and then have a separate routine to get from
6387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            skeleton to pattern.</i>
6397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the input Map containing the values.
6407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
6417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
6427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Map<String, String> getSkeletons(Map<String, String> result) {
6437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (result == null) {
6447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result = new LinkedHashMap<String, String>();
6457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (DateTimeMatcher item : skeleton2pattern.keySet()) {
6477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            PatternWithSkeletonFlag patternWithSkelFlag = skeleton2pattern.get(item);
6487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String pattern = patternWithSkelFlag.pattern;
6497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (CANONICAL_SET.contains(pattern)) {
6507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                continue;
6517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
6527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.put(item.toString(), pattern);
6537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result;
6557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
6587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Return a list of all the base skeletons (in canonical form) from this class
6597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
6607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
6617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Set<String> getBaseSkeletons(Set<String> result) {
6627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (result == null) {
6637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result = new HashSet<String>();
6647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        result.addAll(basePattern_pattern.keySet());
6667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result;
6677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
6707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Adjusts the field types (width and subtype) of a pattern to match what is
6717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * in a skeleton. That is, if you supply a pattern like "d-M H:m", and a
6727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * skeleton of "MMMMddhhmm", then the input pattern is adjusted to be
6737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "dd-MMMM hh:mm". This is used internally to get the best match for the
6747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * input skeleton, but can also be used externally.
6757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>Example code:{@.jcite com.ibm.icu.samples.text.datetimepatterngenerator.DateTimePatternGeneratorSample:---replaceFieldTypesExample}
6767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param pattern input pattern
6777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton For the pattern to match to.
6787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return pattern adjusted to match the skeleton fields widths and subtypes.
6797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
6807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
6817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String replaceFieldTypes(String pattern, String skeleton) {
6827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return replaceFieldTypes(pattern, skeleton, MATCH_NO_OPTIONS);
6837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
6867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Adjusts the field types (width and subtype) of a pattern to match what is
6877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * in a skeleton. That is, if you supply a pattern like "d-M H:m", and a
6887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * skeleton of "MMMMddhhmm", then the input pattern is adjusted to be
6897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "dd-MMMM hh:mm". This is used internally to get the best match for the
6907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * input skeleton, but can also be used externally.
6917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
6927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param pattern input pattern
6937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton For the pattern to match to.
6947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param options MATCH_xxx options for forcing the length of specified fields in
6957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            the returned pattern to match those in the skeleton (when this would
6967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            not happen otherwise). For default behavior, use MATCH_NO_OPTIONS.
6977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return pattern adjusted to match the skeleton fields widths and subtypes.
6987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.4
6997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
7007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String replaceFieldTypes(String pattern, String skeleton, int options) {
7017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        synchronized (this) { // synchronized since a getter must be thread-safe
7027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            PatternWithMatcher patternNoMatcher = new PatternWithMatcher(pattern, null);
7037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return adjustFieldTypes(patternNoMatcher, current.set(skeleton, fp, false), EnumSet.noneOf(DTPGflags.class), options);
7047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
7087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The date time format is a message format pattern used to compose date and
7097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * time patterns. The default value is "{1} {0}", where {1} will be replaced
7107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * by the date pattern and {0} will be replaced by the time pattern.
7117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>
7127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is used when the input skeleton contains both date and time fields,
7137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * but there is not a close match among the added patterns. For example,
7147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * suppose that this object was created by adding "dd-MMM" and "hh:mm", and
7157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * its datetimeFormat is the default "{1} {0}". Then if the input skeleton
7167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * is "MMMdhmm", there is not an exact match, so the input skeleton is
7177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * broken up into two components "MMMd" and "hmm". There are close matches
7187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * for those two skeletons, so the result is put together with this pattern,
7197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * resulting in "d-MMM h:mm".
7207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
7217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param dateTimeFormat message format pattern, where {1} will be replaced by the date
7227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            pattern and {0} will be replaced by the time pattern.
7237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
7247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
7257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void setDateTimeFormat(String dateTimeFormat) {
7267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        checkFrozen();
7277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        this.dateTimeFormat = dateTimeFormat;
7287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
7317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Getter corresponding to setDateTimeFormat.
7327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
7337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return pattern
7347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
7357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
7367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getDateTimeFormat() {
7377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return dateTimeFormat;
7387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
7417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The decimal value is used in formatting fractions of seconds. If the
7427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * skeleton contains fractional seconds, then this is used with the
7437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * fractional seconds. For example, suppose that the input pattern is
7447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "hhmmssSSSS", and the best matching pattern internally is "H:mm:ss", and
7457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the decimal string is ",". Then the resulting pattern is modified to be
7467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "H:mm:ss,SSSS"
7477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
7487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param decimal The decimal to set to.
7497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
7507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
7517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void setDecimal(String decimal) {
7527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        checkFrozen();
7537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        this.decimal = decimal;
7547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
7577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Getter corresponding to setDecimal.
7587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return string corresponding to the decimal point
7597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
7607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
7617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getDecimal() {
7627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return decimal;
7637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
7667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Redundant patterns are those which if removed, make no difference in the
7677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * resulting getBestPattern values. This method returns a list of them, to
7687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * help check the consistency of the patterns used to build this generator.
7697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
7707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param output stores the redundant patterns that are removed. To get these
7717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            in internal order, supply a LinkedHashSet. If null, a
7727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            collection is allocated.
7737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the collection with added elements.
7747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
7757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
7767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
7777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
7787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Collection<String> getRedundants(Collection<String> output) {
7797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        synchronized (this) { // synchronized since a getter must be thread-safe
7807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (output == null) {
7817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                output = new LinkedHashSet<String>();
7827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (DateTimeMatcher cur : skeleton2pattern.keySet()) {
7847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                PatternWithSkeletonFlag patternWithSkelFlag = skeleton2pattern.get(cur);
7857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String pattern = patternWithSkelFlag.pattern;
7867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (CANONICAL_SET.contains(pattern)) {
7877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    continue;
7887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
7897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String trial = getBestPattern(cur.toString(), cur, MATCH_NO_OPTIONS);
7907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (trial.equals(pattern)) {
7917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    output.add(pattern);
7927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
7937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ///CLOVER:OFF
7957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //The following would never be called since the parameter is false
7967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //Eclipse stated the following is "dead code"
7977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /*if (false) { // ordered
7987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                DateTimePatternGenerator results = new DateTimePatternGenerator();
7997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                PatternInfo pinfo = new PatternInfo();
8007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (DateTimeMatcher cur : skeleton2pattern.keySet()) {
8017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String pattern = skeleton2pattern.get(cur);
8027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (CANONICAL_SET.contains(pattern)) {
8037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        continue;
8047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
8057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //skipMatcher = current;
8067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String trial = results.getBestPattern(cur.toString());
8077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (trial.equals(pattern)) {
8087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        output.add(pattern);
8097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else {
8107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        results.addPattern(pattern, false, pinfo);
8117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
8127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
8137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }*/
8147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ///CLOVER:ON
8157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return output;
8167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Field numbers, used for AppendItem functions
8207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
8237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final public int ERA = 0;
8257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
8287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final public int YEAR = 1;
8307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
8337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final public int QUARTER = 2;
8357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
8387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final public int MONTH = 3;
8407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
8437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final public int WEEK_OF_YEAR = 4;
8457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
8487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final public int WEEK_OF_MONTH = 5;
8507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
8537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final public int WEEKDAY = 6;
8557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
8587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final public int DAY = 7;
8607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
8637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final public int DAY_OF_YEAR = 8;
8657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
8687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final public int DAY_OF_WEEK_IN_MONTH = 9;
8707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
8737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final public int DAYPERIOD = 10;
8757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
8787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final public int HOUR = 11;
8807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
8837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final public int MINUTE = 12;
8857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
8887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final public int SECOND = 13;
8907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
8937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final public int FRACTIONAL_SECOND = 14;
8957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
8987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final public int ZONE = 15;
9007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
9037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final public int TYPE_LIMIT = 16;
9057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Option masks for getBestPattern, replaceFieldTypes (individual masks may be ORed together)
9077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Default option mask used for {@link #getBestPattern(String, int)}
9107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and {@link #replaceFieldTypes(String, String, int)}.
9117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.4
9127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getBestPattern(String, int)
9137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #replaceFieldTypes(String, String, int)
9147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final int MATCH_NO_OPTIONS = 0;
9167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Option mask for forcing the width of hour field.
9197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.4
9207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getBestPattern(String, int)
9217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #replaceFieldTypes(String, String, int)
9227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final int MATCH_HOUR_FIELD_LENGTH = 1 << HOUR;
9247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Option mask for forcing  the width of minute field.
9277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
9287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
9297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
9317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final int MATCH_MINUTE_FIELD_LENGTH = 1 << MINUTE;
9327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Option mask for forcing  the width of second field.
9357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
9367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
9377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
9397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final int MATCH_SECOND_FIELD_LENGTH = 1 << SECOND;
9407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Option mask for forcing the width of all date and time fields.
9437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.4
9447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getBestPattern(String, int)
9457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #replaceFieldTypes(String, String, int)
9467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final int MATCH_ALL_FIELDS_LENGTH = (1 << TYPE_LIMIT) - 1;
9487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * An AppendItem format is a pattern used to append a field if there is no
9517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * good match. For example, suppose that the input skeleton is "GyyyyMMMd",
9527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and there is no matching pattern internally, but there is a pattern
9537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * matching "yyyyMMMd", say "d-MM-yyyy". Then that pattern is used, plus the
9547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * G. The way these two are conjoined is by using the AppendItemFormat for G
9557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * (era). So if that value is, say "{0}, {1}" then the final resulting
9567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * pattern is "d-MM-yyyy, G".
9577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>
9587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * There are actually three available variables: {0} is the pattern so far,
9597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {1} is the element we are adding, and {2} is the name of the element.
9607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>
9617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This reflects the way that the CLDR data is organized.
9627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
9637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param field such as ERA
9647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param value pattern, such as "{0}, {1}"
9657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
9667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void setAppendItemFormat(int field, String value) {
9687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        checkFrozen();
9697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        appendItemFormats[field] = value;
9707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Getter corresponding to setAppendItemFormats. Values below 0 or at or
9747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * above TYPE_LIMIT are illegal arguments.
9757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
9767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param field The index to retrieve the append item formats.
9777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return append pattern for field
9787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
9797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getAppendItemFormat(int field) {
9817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return appendItemFormats[field];
9827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Sets the names of fields, eg "era" in English for ERA. These are only
9867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * used if the corresponding AppendItemFormat is used, and if it contains a
9877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {2} variable.
9887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>
9897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This reflects the way that the CLDR data is organized.
9907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
9917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param field Index of the append item names.
9927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param value The value to set the item to.
9937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
9947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void setAppendItemName(int field, String value) {
9967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        checkFrozen();
9977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        appendItemNames[field] = value;
9987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
10017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Getter corresponding to setAppendItemNames. Values below 0 or at or above
10027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * TYPE_LIMIT are illegal arguments.
10037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
10047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param field The index to get the append item name.
10057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return name for field
10067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
10077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getAppendItemName(int field) {
10097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return appendItemNames[field];
10107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
10137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Determines whether a skeleton contains a single field
10147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
10157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton The skeleton to determine if it contains a single field.
10167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return true or not
10177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
10187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
10197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
10217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static boolean isSingleField(String skeleton) {
10227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        char first = skeleton.charAt(0);
10237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 1; i < skeleton.length(); ++i) {
10247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (skeleton.charAt(i) != first) return false;
10257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return true;
10277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
10307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Add key to HashSet cldrAvailableFormatKeys.
10317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
10327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param key of the availableFormats in CLDR
10337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
10347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void setAvailableFormat(String key) {
10367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        checkFrozen();
10377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        cldrAvailableFormatKeys.add(key);
10387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
10417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This function checks the corresponding slot of CLDR_AVAIL_FORMAT_KEY[]
10427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * has been added to DateTimePatternGenerator.
10437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The function is to avoid the duplicate availableFomats added to
10447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the pattern map from parent locales.
10457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
10467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param key of the availableFormatMask in CLDR
10477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return TRUE if the corresponding slot of CLDR_AVAIL_FORMAT_KEY[]
10487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * has been added to DateTimePatternGenerator.
10497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
10507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private boolean isAvailableFormatSet(String key) {
10527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return cldrAvailableFormatKeys.contains(key);
10537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
10567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Boilerplate for Freezable
10577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
10587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean isFrozen() {
10607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return frozen;
10617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
10647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Boilerplate for Freezable
10657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.4
10667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public DateTimePatternGenerator freeze() {
10687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        frozen = true;
10697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return this;
10707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
10737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Boilerplate for Freezable
10747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.4
10757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public DateTimePatternGenerator cloneAsThawed() {
10777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DateTimePatternGenerator result = (DateTimePatternGenerator) (this.clone());
10787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        frozen = false;
10797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result;
10807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
10837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Boilerplate
10847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.6
10857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @SuppressWarnings("unchecked")
10877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Object clone() {
10887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        try {
10897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            DateTimePatternGenerator result = (DateTimePatternGenerator) (super.clone());
10907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.skeleton2pattern = (TreeMap<DateTimeMatcher, PatternWithSkeletonFlag>) skeleton2pattern.clone();
10917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.basePattern_pattern = (TreeMap<String, PatternWithSkeletonFlag>) basePattern_pattern.clone();
10927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.appendItemFormats = appendItemFormats.clone();
10937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.appendItemNames = appendItemNames.clone();
10947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.current = new DateTimeMatcher();
10957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.fp = new FormatParser();
10967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result._distanceInfo = new DistanceInfo();
10977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.frozen = false;
10997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return result;
11007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } catch (CloneNotSupportedException e) {
11017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ///CLOVER:OFF
11027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new ICUCloneNotSupportedException("Internal Error", e);
11037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ///CLOVER:ON
11047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
11057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
11067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
11087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Utility class for FormatParser. Immutable class that is only used to mark
11097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the difference between a variable field and a literal string. Each
11107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * variable field must consist of 1 to n variable characters, representing
11117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * date format fields. For example, "VVVV" is valid while "V4" is not, nor
11127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * is "44".
11137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
11147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
11157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
11167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
11177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
11187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static class VariableField {
11197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final String string;
11207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final int canonicalIndex;
11217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
11237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Create a variable field: equivalent to VariableField(string,false);
11247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param string The string for the variable field.
11257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
11267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
11277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
11287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
11297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public VariableField(String string) {
11307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this(string, false);
11317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
11327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
11337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Create a variable field
11347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param string The string for the variable field
11357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param strict If true, then only allows exactly those lengths specified by CLDR for variables. For example, "hh:mm aa" would throw an exception.
11367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @throws IllegalArgumentException if the variable field is not valid.
11377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
11387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
11397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
11407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
11417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public VariableField(String string, boolean strict) {
11427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            canonicalIndex = DateTimePatternGenerator.getCanonicalIndex(string, strict);
11437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (canonicalIndex < 0) {
11447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new IllegalArgumentException("Illegal datetime field:\t"
11457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        + string);
11467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
11477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.string = string;
11487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
11497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
11517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Get the main type of this variable. These types are ERA, QUARTER,
11527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * MONTH, DAY, WEEK_OF_YEAR, WEEK_OF_MONTH, WEEKDAY, DAY, DAYPERIOD
11537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * (am/pm), HOUR, MINUTE, SECOND,FRACTIONAL_SECOND, ZONE.
11547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return main type.
11557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
11567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
11577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
11587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
11597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int getType() {
11607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return types[canonicalIndex][1];
11617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
11627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
11647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
11657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
11667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
11677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
11687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public static String getCanonicalCode(int type) {
11697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            try {
11707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return CANONICAL_ITEMS[type];
11717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } catch (Exception e) {
11727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return String.valueOf(type);
11737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
11747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
11757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
11767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Check if the type of this variable field is numeric.
11777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return true if the type of this variable field is numeric.
11787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
11797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
11807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
11817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
11827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean isNumeric() {
11837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return types[canonicalIndex][2] > 0;
11847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
11857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
11877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Private method.
11887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
11897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private int getCanonicalIndex() {
11907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return canonicalIndex;
11917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
11927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
11947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Get the string represented by this variable.
11957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
11967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
11977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
11987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
11997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String toString() {
12007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return string;
12017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
12027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
12037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
12057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This class provides mechanisms for parsing a SimpleDateFormat pattern
12067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * or generating a new pattern, while handling the quoting. It represents
12077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the result of the parse as a list of items, where each item is either a
12087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * literal string or a variable field. When parsing It can be used to find
12097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * out which variable fields are in a date format, and in what order, such
12107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * as for presentation in a UI as separate text entry fields. It can also be
12117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * used to construct new SimpleDateFormats.
12127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>Example:
12137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <pre>
12147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean containsZone(String pattern) {
12157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (Iterator it = formatParser.set(pattern).getItems().iterator(); it.hasNext();) {
12167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Object item = it.next();
12177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (item instanceof VariableField) {
12187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                VariableField variableField = (VariableField) item;
12197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (variableField.getType() == DateTimePatternGenerator.ZONE) {
12207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return true;
12217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
12227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
12237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
12247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return false;
12257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
12267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  </pre>
12277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
12287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
12297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
12307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
12317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static public class FormatParser {
12327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static final UnicodeSet SYNTAX_CHARS = new UnicodeSet("[a-zA-Z]").freeze();
12337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static final UnicodeSet QUOTING_CHARS = new UnicodeSet("[[[:script=Latn:][:script=Cyrl:]]&[[:L:][:M:]]]").freeze();
12347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private transient PatternTokenizer tokenizer = new PatternTokenizer()
12357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        .setSyntaxCharacters(SYNTAX_CHARS)
12367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        .setExtraQuotingCharacters(QUOTING_CHARS)
12377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        .setUsingQuote(true);
12387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private List<Object> items = new ArrayList<Object>();
12397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
12417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Construct an empty date format parser, to which strings and variables can be added with set(...).
12427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
12437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
12447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
12457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
12467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public FormatParser() {
12477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
12487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
12507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Parses the string into a list of items.
12517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param string The string to parse.
12527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return this, for chaining
12537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
12547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
12557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
12567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
12577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final public FormatParser set(String string) {
12587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return set(string, false);
12597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
12607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
12627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Parses the string into a list of items, taking into account all of the quoting that may be going on.
12637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param string  The string to parse.
12647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param strict If true, then only allows exactly those lengths specified by CLDR for variables. For example, "hh:mm aa" would throw an exception.
12657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return this, for chaining
12667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
12677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
12687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
12697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
12707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public FormatParser set(String string, boolean strict) {
12717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            items.clear();
12727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (string.length() == 0) return this;
12737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            tokenizer.setPattern(string);
12747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            StringBuffer buffer = new StringBuffer();
12757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            StringBuffer variable = new StringBuffer();
12767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            while (true) {
12777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buffer.setLength(0);
12787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int status = tokenizer.next(buffer);
12797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (status == PatternTokenizer.DONE) break;
12807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (status == PatternTokenizer.SYNTAX) {
12817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (variable.length() != 0 && buffer.charAt(0) != variable.charAt(0)) {
12827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        addVariable(variable, false);
12837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
12847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    variable.append(buffer);
12857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
12867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    addVariable(variable, false);
12877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    items.add(buffer.toString());
12887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
12897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
12907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            addVariable(variable, false);
12917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return this;
12927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
12937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private void addVariable(StringBuffer variable, boolean strict) {
12957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (variable.length() != 0) {
12967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                items.add(new VariableField(variable.toString(), strict));
12977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                variable.setLength(0);
12987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
12997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //        /** Private method. Return a collection of fields. These will be a mixture of literal Strings and VariableFields. Any "a" variable field is removed.
13027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //         * @param output List to append the items to. If null, is allocated as an ArrayList.
13037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //         * @return list
13047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //         */
13057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //        private List getVariableFields(List output) {
13067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            if (output == null) output = new ArrayList();
13077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            main:
13087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                for (Iterator it = items.iterator(); it.hasNext();) {
13097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    Object item = it.next();
13107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    if (item instanceof VariableField) {
13117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        String s = item.toString();
13127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        switch(s.charAt(0)) {
13137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        //case 'Q': continue main; // HACK
13147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        case 'a': continue main; // remove
13157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        }
13167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        output.add(item);
13177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    }
13187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                }
13197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            //System.out.println(output);
13207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            return output;
13217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //        }
13227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //        /**
13247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //         * Produce a string which concatenates all the variables. That is, it is the logically the same as the input with all literals removed.
13257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //         * @return a string which is a concatenation of all the variable fields
13267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //         */
13277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //        public String getVariableFieldString() {
13287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            List list = getVariableFields(null);
13297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            StringBuffer result = new StringBuffer();
13307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            for (Iterator it = list.iterator(); it.hasNext();) {
13317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                String item = it.next().toString();
13327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                result.append(item);
13337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            }
13347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            return result.toString();
13357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //        }
13367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
13387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Returns modifiable list which is a mixture of Strings and VariableFields, in the order found during parsing. The strings represent literals, and have all quoting removed. Thus the string "dd 'de' MM" will parse into three items:
13397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * <pre>
13407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * VariableField: dd
13417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * String: " de "
13427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * VariableField: MM
13437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * </pre>
13447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * The list is modifiable, so you can add any strings or variables to it, or remove any items.
13457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return modifiable list of items.
13467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
13477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
13487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
13497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
13507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public List<Object> getItems() {
13517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return items;
13527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /** Provide display form of formatted input. Each literal string is quoted if necessary.. That is, if the input was "hh':'mm", the result would be "hh:mm", since the ":" doesn't need quoting. See quoteLiteral().
13557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return printable output string
13567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
13577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
13587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
13597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
13607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String toString() {
13617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return toString(0, items.size());
13627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
13657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Provide display form of a segment of the parsed input. Each literal string is minimally quoted. That is, if the input was "hh':'mm", the result would be "hh:mm", since the ":" doesn't need quoting. See quoteLiteral().
13667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param start item to start from
13677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param limit last item +1
13687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return printable output string
13697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
13707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
13717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
13727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
13737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String toString(int start, int limit) {
13747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            StringBuilder result = new StringBuilder();
13757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = start; i < limit; ++i) {
13767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Object item = items.get(i);
13777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (item instanceof String) {
13787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String itemString = (String) item;
13797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    result.append(tokenizer.quoteLiteral(itemString));
13807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
13817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    result.append(items.get(i).toString());
13827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
13837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
13847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return result.toString();
13857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
13887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Returns true if it has a mixture of date and time variable fields: that is, at least one date variable and at least one time variable.
13897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return true or false
13907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
13917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
13927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
13937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
13947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean hasDateAndTimeFields() {
13957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int foundMask = 0;
13967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (Object item : items) {
13977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (item instanceof VariableField) {
13987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    int type = ((VariableField)item).getType();
13997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    foundMask |= 1 << type;
14007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
14017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
14027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean isDate = (foundMask & DATE_MASK) != 0;
14037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean isTime = (foundMask & TIME_MASK) != 0;
14047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return isDate && isTime;
14057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //        /**
14087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //         * Internal routine
14097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //         * @param value
14107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //         * @param result
14117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //         * @return list
14127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //         */
14137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //        public List getAutoPatterns(String value, List result) {
14147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            if (result == null) result = new ArrayList();
14157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            int fieldCount = 0;
14167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            int minField = Integer.MAX_VALUE;
14177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            int maxField = Integer.MIN_VALUE;
14187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            for (Iterator it = items.iterator(); it.hasNext();) {
14197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                Object item = it.next();
14207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                if (item instanceof VariableField) {
14217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    try {
14227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        int type = ((VariableField)item).getType();
14237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        if (minField > type) minField = type;
14247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        if (maxField < type) maxField = type;
14257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        if (type == ZONE || type == DAYPERIOD || type == WEEKDAY) return result; // skip anything with zones
14267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        fieldCount++;
14277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    } catch (Exception e) {
14287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        return result; // if there are any funny fields, return
14297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    }
14307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                }
14317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            }
14327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            if (fieldCount < 3) return result; // skip
14337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            // trim from start
14347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            // trim first field IF there are no letters around it
14357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            // and it is either the min or the max field
14367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            // first field is either 0 or 1
14377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            for (int i = 0; i < items.size(); ++i) {
14387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                Object item = items.get(i);
14397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                if (item instanceof VariableField) {
14407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    int type = ((VariableField)item).getType();
14417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    if (type != minField && type != maxField) break;
14427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //
14437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    if (i > 0) {
14447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        Object previousItem = items.get(0);
14457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        if (alpha.containsSome(previousItem.toString())) break;
14467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    }
14477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    int start = i+1;
14487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    if (start < items.size()) {
14497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        Object nextItem = items.get(start);
14507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        if (nextItem instanceof String) {
14517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                            if (alpha.containsSome(nextItem.toString())) break;
14527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                            start++; // otherwise skip over string
14537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        }
14547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    }
14557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    result.add(toString(start, items.size()));
14567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    break;
14577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                }
14587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            }
14597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            // now trim from end
14607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            for (int i = items.size()-1; i >= 0; --i) {
14617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                Object item = items.get(i);
14627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                if (item instanceof VariableField) {
14637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    int type = ((VariableField)item).getType();
14647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    if (type != minField && type != maxField) break;
14657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    if (i < items.size() - 1) {
14667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        Object previousItem = items.get(items.size() - 1);
14677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        if (alpha.containsSome(previousItem.toString())) break;
14687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    }
14697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    int end = i-1;
14707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    if (end > 0) {
14717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        Object nextItem = items.get(end);
14727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        if (nextItem instanceof String) {
14737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                            if (alpha.containsSome(nextItem.toString())) break;
14747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                            end--; // otherwise skip over string
14757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        }
14767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    }
14777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    result.add(toString(0, end+1));
14787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                    break;
14797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                }
14807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            }
14817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //
14827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            return result;
14837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //        }
14847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //        private static UnicodeSet alpha = new UnicodeSet("[:alphabetic:]");
14867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //        private int getType(Object item) {
14887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            String s = item.toString();
14897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            int canonicalIndex = getCanonicalIndex(s);
14907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            if (canonicalIndex < 0) {
14917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                throw new IllegalArgumentException("Illegal field:\t"
14927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //                        + s);
14937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            }
14947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            int type = types[canonicalIndex][1];
14957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //            return type;
14967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //        }
14977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
14997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *  Each literal string is quoted as needed. That is, the ' quote marks will only be added if needed. The exact pattern of quoting is not guaranteed, thus " de la " could be quoted as " 'de la' " or as " 'de' 'la' ".
15007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param string The string to check.
15017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return string with quoted literals
15027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
15037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
15047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
15057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
15067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Object quoteLiteral(String string) {
15077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return tokenizer.quoteLiteral(string);
15087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
15137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    * Used by CLDR tooling; not in ICU4C.
15147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    * Note, this will not work correctly with normal skeletons, since fields
15157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    * that should be related in the two skeletons being compared - like EEE and
15167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    * ccc, or y and U - will not be sorted in the same relative place as each
15177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    * other when iterating over both TreeSets being compare, using TreeSet's
15187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    * "natural" code point ordering (this could be addressed by initializing
15197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    * the TreeSet with a comparator that compares fields first by their index
15207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    * from getCanonicalIndex()). However if comparing canonical skeletons from
15217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    * getCanonicalSkeletonAllowingDuplicates it will be OK regardless, since
15227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    * in these skeletons all fields are normalized to the canonical pattern
15237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    * char for those fields - M or L to M, E or c to E, y or U to y, etc. -
15247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    * so corresponding fields will sort in the same way for both TreeMaps.
15257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    * @internal
15267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    * @deprecated This API is ICU internal only.
15277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    */
15287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
15297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean skeletonsAreSimilar(String id, String skeleton) {
15307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (id.equals(skeleton)) {
15317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return true; // fast path
15327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // must clone array, make sure items are in same order.
15347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        TreeSet<String> parser1 = getSet(id);
15357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        TreeSet<String> parser2 = getSet(skeleton);
15367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (parser1.size() != parser2.size()) {
15377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return false;
15387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Iterator<String> it2 = parser2.iterator();
15407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (String item : parser1) {
15417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int index1 = getCanonicalIndex(item, false);
15427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String item2 = it2.next(); // same length so safe
15437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int index2 = getCanonicalIndex(item2, false);
15447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (types[index1][1] != types[index2][1]) {
15457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return false;
15467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
15477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return true;
15497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private TreeSet<String> getSet(String id) {
15527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final List<Object> items = fp.set(id).getItems();
15537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        TreeSet<String> result = new TreeSet<String>();
15547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (Object obj : items) {
15557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            final String item = obj.toString();
15567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (item.startsWith("G") || item.startsWith("a")) {
15577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                continue;
15587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
15597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.add(item);
15607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result;
15627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // ========= PRIVATES ============
15657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static class PatternWithMatcher {
15677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String pattern;
15687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public DateTimeMatcher matcherWithSkeleton;
15697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Simple constructor
15707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public PatternWithMatcher(String pat, DateTimeMatcher matcher) {
15717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            pattern = pat;
15727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            matcherWithSkeleton = matcher;
15737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static class PatternWithSkeletonFlag {
15767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String pattern;
15777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean skeletonWasSpecified;
15787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Simple constructor
15797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public PatternWithSkeletonFlag(String pat, boolean skelSpecified) {
15807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            pattern = pat;
15817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            skeletonWasSpecified = skelSpecified;
15827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String toString() {
15847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return pattern + "," + skeletonWasSpecified;
15857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private TreeMap<DateTimeMatcher, PatternWithSkeletonFlag> skeleton2pattern = new TreeMap<DateTimeMatcher, PatternWithSkeletonFlag>(); // items are in priority order
15887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private TreeMap<String, PatternWithSkeletonFlag> basePattern_pattern = new TreeMap<String, PatternWithSkeletonFlag>(); // items are in priority order
15897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private String decimal = "?";
15907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private String dateTimeFormat = "{1} {0}";
15917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private String[] appendItemFormats = new String[TYPE_LIMIT];
15927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private String[] appendItemNames = new String[TYPE_LIMIT];
15937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
15947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < TYPE_LIMIT; ++i) {
15957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            appendItemFormats[i] = "{0} \u251C{2}: {1}\u2524";
15967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            appendItemNames[i] = "F" + i;
15977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private char defaultHourFormatChar = 'H';
16007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //private boolean chineseMonthHack = false;
16017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //private boolean isComplete = false;
16027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private volatile boolean frozen = false;
16037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private transient DateTimeMatcher current = new DateTimeMatcher();
16057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private transient FormatParser fp = new FormatParser();
16067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private transient DistanceInfo _distanceInfo = new DistanceInfo();
16077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final int FRACTIONAL_MASK = 1<<FRACTIONAL_SECOND;
16097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final int SECOND_AND_FRACTIONAL_MASK = (1<<SECOND) | (1<<FRACTIONAL_SECOND);
16107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Cache for DateTimePatternGenerator
16127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static ICUCache<String, DateTimePatternGenerator> DTPNG_CACHE = new SimpleCache<String, DateTimePatternGenerator>();
16137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void checkFrozen() {
16157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (isFrozen()) {
16167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new UnsupportedOperationException("Attempt to modify frozen object");
16177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
16197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
16217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * We only get called here if we failed to find an exact skeleton. We have broken it into date + time, and look for the pieces.
16227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If we fail to find a complete skeleton, we compose in a loop until we have all the fields.
16237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
16247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private String getBestAppending(DateTimeMatcher source, int missingFields, DistanceInfo distInfo, DateTimeMatcher skipMatcher, EnumSet<DTPGflags> flags, int options) {
16257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String resultPattern = null;
16267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (missingFields != 0) {
16277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            PatternWithMatcher resultPatternWithMatcher = getBestRaw(source, missingFields, distInfo, skipMatcher);
16287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            resultPattern = adjustFieldTypes(resultPatternWithMatcher, source, flags, options);
16297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            while (distInfo.missingFieldMask != 0) { // precondition: EVERY single field must work!
16317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // special hack for SSS. If we are missing SSS, and we had ss but found it, replace the s field according to the
16337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // number separator
16347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ((distInfo.missingFieldMask & SECOND_AND_FRACTIONAL_MASK) == FRACTIONAL_MASK
16357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        && (missingFields & SECOND_AND_FRACTIONAL_MASK) == SECOND_AND_FRACTIONAL_MASK) {
16367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    resultPatternWithMatcher.pattern = resultPattern;
16377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    flags = EnumSet.copyOf(flags);
16387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    flags.add(DTPGflags.FIX_FRACTIONAL_SECONDS);
16397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    resultPattern = adjustFieldTypes(resultPatternWithMatcher, source, flags, options);
16407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    distInfo.missingFieldMask &= ~FRACTIONAL_MASK; // remove bit
16417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    continue;
16427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
16437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int startingMask = distInfo.missingFieldMask;
16457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                PatternWithMatcher tempWithMatcher = getBestRaw(source, distInfo.missingFieldMask, distInfo, skipMatcher);
16467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String temp = adjustFieldTypes(tempWithMatcher, source, flags, options);
16477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int foundMask = startingMask & ~distInfo.missingFieldMask;
16487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int topField = getTopBitNumber(foundMask);
16497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                resultPattern = MessageFormat.format(getAppendFormat(topField), new Object[]{resultPattern, temp, getAppendName(topField)});
16507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
16517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return resultPattern;
16537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
16547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private String getAppendName(int foundMask) {
16567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return "'" + appendItemNames[foundMask] + "'";
16577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
16587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private String getAppendFormat(int foundMask) {
16597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return appendItemFormats[foundMask];
16607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
16617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //    /**
16637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //     * @param current2
16647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //     * @return
16657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //     */
16667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //    private String adjustSeconds(DateTimeMatcher current2) {
16677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //        // TODO Auto-generated method stub
16687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //        return null;
16697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //    }
16707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
16727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param foundMask
16737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
16747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private int getTopBitNumber(int foundMask) {
16757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int i = 0;
16767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        while (foundMask != 0) {
16777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            foundMask >>>= 1;
16787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ++i;
16797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return i-1;
16817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
16827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
16847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
16857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
16867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void complete() {
16877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PatternInfo patternInfo = new PatternInfo();
16887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // make sure that every valid field occurs once, with a "default" length
16897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < CANONICAL_ITEMS.length; ++i) {
16907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //char c = (char)types[i][0];
16917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            addPattern(String.valueOf(CANONICAL_ITEMS[i]), false, patternInfo);
16927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //isComplete = true;
16947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
16957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
16967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        complete();
16977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
16987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
17007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
17017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
17027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private PatternWithMatcher getBestRaw(DateTimeMatcher source, int includeMask, DistanceInfo missingFields, DateTimeMatcher skipMatcher) {
17037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //      if (SHOW_DISTANCE) System.out.println("Searching for: " + source.pattern
17047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //      + ", mask: " + showMask(includeMask));
17057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int bestDistance = Integer.MAX_VALUE;
17067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PatternWithMatcher bestPatternWithMatcher = new PatternWithMatcher("", null);
17077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DistanceInfo tempInfo = new DistanceInfo();
17087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (DateTimeMatcher trial : skeleton2pattern.keySet()) {
17097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (trial.equals(skipMatcher)) {
17107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                continue;
17117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int distance = source.getDistance(trial, includeMask, tempInfo);
17137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //          if (SHOW_DISTANCE) System.out.println("\tDistance: " + trial.pattern + ":\t"
17147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //          + distance + ",\tmissing fields: " + tempInfo);
17157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (distance < bestDistance) {
17167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                bestDistance = distance;
17177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                PatternWithSkeletonFlag patternWithSkelFlag = skeleton2pattern.get(trial);
17187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                bestPatternWithMatcher.pattern = patternWithSkelFlag.pattern;
17197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // If the best raw match had a specified skeleton then return it too.
17207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // This can be passed through to adjustFieldTypes to help it do a better job.
17217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (patternWithSkelFlag.skeletonWasSpecified) {
17227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    bestPatternWithMatcher.matcherWithSkeleton = trial;
17237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
17247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    bestPatternWithMatcher.matcherWithSkeleton = null;
17257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
17267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                missingFields.setTo(tempInfo);
17277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (distance == 0) {
17287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    break;
17297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
17307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
17327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return bestPatternWithMatcher;
17337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
17347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
17367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param fixFractionalSeconds TODO
17377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
17387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // flags values
17397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private enum DTPGflags { FIX_FRACTIONAL_SECONDS, SKELETON_USES_CAP_J };
17407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private String adjustFieldTypes(PatternWithMatcher patternWithMatcher, DateTimeMatcher inputRequest, EnumSet<DTPGflags> flags, int options) {
17427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fp.set(patternWithMatcher.pattern);
17437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder newPattern = new StringBuilder();
17447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (Object item : fp.getItems()) {
17457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (item instanceof String) {
17467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                newPattern.append(fp.quoteLiteral((String)item));
17477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
17487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                final VariableField variableField = (VariableField) item;
17497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                StringBuilder fieldBuilder = new StringBuilder(variableField.toString());
17507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //                int canonicalIndex = getCanonicalIndex(field, true);
17517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //                if (canonicalIndex < 0) {
17527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //                    continue; // don't adjust
17537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //                }
17547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //                int type = types[canonicalIndex][1];
17557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int type = variableField.getType();
17567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (flags.contains(DTPGflags.FIX_FRACTIONAL_SECONDS) && type == SECOND) {
17587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String newField = inputRequest.original[FRACTIONAL_SECOND];
17597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    fieldBuilder.append(decimal);
17607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    fieldBuilder.append(newField);
17617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (inputRequest.type[type] != 0) {
17627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Here:
17637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // - "reqField" is the field from the originally requested skeleton, with length
17647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // "reqFieldLen".
17657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // - "field" is the field from the found pattern.
17667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //
17677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // The adjusted field should consist of characters from the originally requested
17687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // skeleton, except in the case of HOUR or MONTH or WEEKDAY or YEAR, in which case it
17697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // should consist of characters from the found pattern.
17707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //
17717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // The length of the adjusted field (adjFieldLen) should match that in the originally
17727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // requested skeleton, except that in the following cases the length of the adjusted field
17737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // should match that in the found pattern (i.e. the length of this pattern field should
17747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // not be adjusted):
17757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // 1. type is HOUR and the corresponding bit in options is not set (ticket #7180).
17767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //    Note, we may want to implement a similar change for other numeric fields (MM, dd,
17777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //    etc.) so the default behavior is to get locale preference for field length, but
17787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //    options bits can be used to override this.
17797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // 2. There is a specified skeleton for the found pattern and one of the following is true:
17807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //    a) The length of the field in the skeleton (skelFieldLen) is equal to reqFieldLen.
17817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //    b) The pattern field is numeric and the skeleton field is not, or vice versa.
17827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //
17837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Old behavior was:
17847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // normally we just replace the field. However HOUR is special; we only change the length
17857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String reqField = inputRequest.original[type];
17877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    int reqFieldLen = reqField.length();
17887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if ( reqField.charAt(0) == 'E' && reqFieldLen < 3 ) {
17897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        reqFieldLen = 3; // 1-3 for E are equivalent to 3 for c,e
17907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
17917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    int adjFieldLen = reqFieldLen;
17927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    DateTimeMatcher matcherWithSkeleton = patternWithMatcher.matcherWithSkeleton;
17937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if ( (type == HOUR && (options & MATCH_HOUR_FIELD_LENGTH)==0) ||
17947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                         (type == MINUTE && (options & MATCH_MINUTE_FIELD_LENGTH)==0) ||
17957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                         (type == SECOND && (options & MATCH_SECOND_FIELD_LENGTH)==0) ) {
17967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        adjFieldLen = fieldBuilder.length();
17977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else if (matcherWithSkeleton != null) {
17987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        String skelField = matcherWithSkeleton.origStringForField(type);
17997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        int skelFieldLen = skelField.length();
18007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        boolean patFieldIsNumeric = variableField.isNumeric();
18017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        boolean skelFieldIsNumeric = matcherWithSkeleton.fieldIsNumeric(type);
18027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (skelFieldLen == reqFieldLen || (patFieldIsNumeric && !skelFieldIsNumeric) || (skelFieldIsNumeric && !patFieldIsNumeric)) {
18037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // don't adjust the field length in the found pattern
18047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            adjFieldLen = fieldBuilder.length();
18057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
18067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
18077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    char c = (type != HOUR && type != MONTH && type != WEEKDAY && (type != YEAR || reqField.charAt(0)=='Y'))?
18087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                reqField.charAt(0): fieldBuilder.charAt(0);
18097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (type == HOUR && flags.contains(DTPGflags.SKELETON_USES_CAP_J)) {
18107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        c = defaultHourFormatChar;
18117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
18127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    fieldBuilder = new StringBuilder();
18137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    for (int i = adjFieldLen; i > 0; --i) fieldBuilder.append(c);
18147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
18157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                newPattern.append(fieldBuilder);
18167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
18177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
18187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //if (SHOW_DISTANCE) System.out.println("\tRaw: " + pattern);
18197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return newPattern.toString();
18207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //  public static String repeat(String s, int count) {
18237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //  StringBuffer result = new StringBuffer();
18247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //  for (int i = 0; i < count; ++i) {
18257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //  result.append(s);
18267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //  }
18277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //  return result.toString();
18287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //  }
18297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
18317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * internal routine
18327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param pattern The pattern that is passed.
18337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return field value
18347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
18357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
18367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
18377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
18387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getFields(String pattern) {
18397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fp.set(pattern);
18407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder newPattern = new StringBuilder();
18417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (Object item : fp.getItems()) {
18427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (item instanceof String) {
18437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                newPattern.append(fp.quoteLiteral((String)item));
18447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
18457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                newPattern.append("{" + getName(item.toString()) + "}");
18467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
18477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
18487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return newPattern.toString();
18497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String showMask(int mask) {
18527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder result = new StringBuilder();
18537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < TYPE_LIMIT; ++i) {
18547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ((mask & (1<<i)) == 0)
18557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                continue;
18567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (result.length() != 0)
18577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.append(" | ");
18587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.append(FIELD_NAME[i]);
18597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.append(" ");
18607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
18617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result.toString();
18627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final String[] CLDR_FIELD_APPEND = {
18657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "Era", "Year", "Quarter", "Month", "Week", "*", "Day-Of-Week",
18667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "Day", "*", "*", "*",
18677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "Hour", "Minute", "Second", "*", "Timezone"
18687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    };
18697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final String[] CLDR_FIELD_NAME = {
18717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "era", "year", "*", "month", "week", "*", "weekday",
18727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "day", "*", "*", "dayperiod",
18737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "hour", "minute", "second", "*", "zone"
18747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    };
18757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final String[] FIELD_NAME = {
18777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "Era", "Year", "Quarter", "Month", "Week_in_Year", "Week_in_Month", "Weekday",
18787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "Day", "Day_Of_Year", "Day_of_Week_in_Month", "Dayperiod",
18797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "Hour", "Minute", "Second", "Fractional_Second", "Zone"
18807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    };
18817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final String[] CANONICAL_ITEMS = {
18847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "G", "y", "Q", "M", "w", "W", "E",
18857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "d", "D", "F",
18867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "H", "m", "s", "S", "v"
18877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    };
18887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final Set<String> CANONICAL_SET = new HashSet<String>(Arrays.asList(CANONICAL_ITEMS));
18907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private Set<String> cldrAvailableFormatKeys = new HashSet<String>(20);
18917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final int
18937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    DATE_MASK = (1<<DAYPERIOD) - 1,
18947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    TIME_MASK = (1<<TYPE_LIMIT) - 1 - DATE_MASK;
18957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final int // numbers are chosen to express 'distance'
18977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    DELTA = 0x10,
18987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    NUMERIC = 0x100,
18997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    NONE = 0,
19007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    NARROW = -0x101,
19017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    SHORT = -0x102,
19027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    LONG = -0x103,
19037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    EXTRA_FIELD =   0x10000,
19047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    MISSING_FIELD = 0x1000;
19057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static private String getName(String s) {
19087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int i = getCanonicalIndex(s, true);
19097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String name = FIELD_NAME[types[i][1]];
19107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int subtype = types[i][2];
19117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean string = subtype < 0;
19127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (string) subtype = -subtype;
19137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (subtype < 0) name += ":S";
19147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        else name += ":N";
19157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return name;
19167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
19197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Get the canonical index, or return -1 if illegal.
19207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param s
19217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param strict TODO
19227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
19237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static int getCanonicalIndex(String s, boolean strict) {
19247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int len = s.length();
19257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (len == 0) {
19267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return -1;
19277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
19287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int ch = s.charAt(0);
19297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //      verify that all are the same character
19307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 1; i < len; ++i) {
19317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (s.charAt(i) != ch) {
19327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return -1;
19337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
19347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
19357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int bestRow = -1;
19367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < types.length; ++i) {
19377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int[] row = types[i];
19387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (row[0] != ch) continue;
19397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            bestRow = i;
19407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (row[3] > len) continue;
19417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (row[row.length-1] < len) continue;
19427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return i;
19437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
19447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return strict ? -1 : bestRow;
19457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final int[][] types = {
19487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // the order here makes a difference only when searching for single field.
19497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // format is:
19507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // pattern character, main type, weight, min length, weight
19517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'G', ERA, SHORT, 1, 3},
19527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'G', ERA, LONG, 4},
19537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'y', YEAR, NUMERIC, 1, 20},
19557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'Y', YEAR, NUMERIC + DELTA, 1, 20},
19567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'u', YEAR, NUMERIC + 2*DELTA, 1, 20},
1957f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        {'r', YEAR, NUMERIC + 3*DELTA, 1, 20},
19587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'U', YEAR, SHORT, 1, 3},
19597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'U', YEAR, LONG, 4},
19607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'U', YEAR, NARROW, 5},
19617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'Q', QUARTER, NUMERIC, 1, 2},
19637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'Q', QUARTER, SHORT, 3},
19647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'Q', QUARTER, LONG, 4},
19657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'q', QUARTER, NUMERIC + DELTA, 1, 2},
19677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'q', QUARTER, SHORT + DELTA, 3},
19687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'q', QUARTER, LONG + DELTA, 4},
19697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'M', MONTH, NUMERIC, 1, 2},
19717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'M', MONTH, SHORT, 3},
19727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'M', MONTH, LONG, 4},
19737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'M', MONTH, NARROW, 5},
19747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'L', MONTH, NUMERIC + DELTA, 1, 2},
19757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'L', MONTH, SHORT - DELTA, 3},
19767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'L', MONTH, LONG - DELTA, 4},
19777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'L', MONTH, NARROW - DELTA, 5},
19787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'l', MONTH, NUMERIC + DELTA, 1, 1},
19807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'w', WEEK_OF_YEAR, NUMERIC, 1, 2},
19827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'W', WEEK_OF_MONTH, NUMERIC + DELTA, 1},
19837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'E', WEEKDAY, SHORT, 1, 3},
19857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'E', WEEKDAY, LONG, 4},
19867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'E', WEEKDAY, NARROW, 5},
19877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'c', WEEKDAY, NUMERIC + 2*DELTA, 1, 2},
19887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'c', WEEKDAY, SHORT - 2*DELTA, 3},
19897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'c', WEEKDAY, LONG - 2*DELTA, 4},
19907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'c', WEEKDAY, NARROW - 2*DELTA, 5},
19917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'e', WEEKDAY, NUMERIC + DELTA, 1, 2}, // 'e' is currently not used in CLDR data, should not be canonical
19927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'e', WEEKDAY, SHORT - DELTA, 3},
19937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'e', WEEKDAY, LONG - DELTA, 4},
19947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'e', WEEKDAY, NARROW - DELTA, 5},
19957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'d', DAY, NUMERIC, 1, 2},
19977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'D', DAY_OF_YEAR, NUMERIC + DELTA, 1, 3},
19987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'F', DAY_OF_WEEK_IN_MONTH, NUMERIC + 2*DELTA, 1},
19997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'g', DAY, NUMERIC + 3*DELTA, 1, 20}, // really internal use, so we don't care
20007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'a', DAYPERIOD, SHORT, 1},
20027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'H', HOUR, NUMERIC + 10*DELTA, 1, 2}, // 24 hour
20047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'k', HOUR, NUMERIC + 11*DELTA, 1, 2},
20057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'h', HOUR, NUMERIC, 1, 2}, // 12 hour
20067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'K', HOUR, NUMERIC + DELTA, 1, 2},
20077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'m', MINUTE, NUMERIC, 1, 2},
20097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'s', SECOND, NUMERIC, 1, 2},
20117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'S', FRACTIONAL_SECOND, NUMERIC + DELTA, 1, 1000},
20127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'A', SECOND, NUMERIC + 2*DELTA, 1, 1000},
20137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'v', ZONE, SHORT - 2*DELTA, 1},
20157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'v', ZONE, LONG - 2*DELTA, 4},
20167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'z', ZONE, SHORT, 1, 3},
20177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'z', ZONE, LONG, 4},
20187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'Z', ZONE, NARROW - DELTA, 1, 3},
20197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'Z', ZONE, LONG - DELTA, 4},
20207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'Z', ZONE, SHORT - DELTA, 5},
20217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'O', ZONE, SHORT - DELTA, 1},
20227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'O', ZONE, LONG - DELTA, 4},
20237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'V', ZONE, SHORT - DELTA, 1},
20247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'V', ZONE, LONG - DELTA, 2},
20257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'X', ZONE, NARROW - DELTA, 1},
20267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'X', ZONE, SHORT - DELTA, 2},
20277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'X', ZONE, LONG - DELTA, 4},
20287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'x', ZONE, NARROW - DELTA, 1},
20297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'x', ZONE, SHORT - DELTA, 2},
20307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {'x', ZONE, LONG - DELTA, 4},
20317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    };
20327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static class DateTimeMatcher implements Comparable<DateTimeMatcher> {
20347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //private String pattern = null;
20357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private int[] type = new int[TYPE_LIMIT];
20367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private String[] original = new String[TYPE_LIMIT];
20377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private String[] baseOriginal = new String[TYPE_LIMIT];
20387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // just for testing; fix to make multi-threaded later
20407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // private static FormatParser fp = new FormatParser();
20417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String origStringForField(int field) {
20437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return original[field];
20447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
20457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean fieldIsNumeric(int field) {
20477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return type[field] > 0;
20487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
20497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String toString() {
20517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            StringBuilder result = new StringBuilder();
20527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = 0; i < TYPE_LIMIT; ++i) {
20537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (original[i].length() != 0) result.append(original[i]);
20547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
20557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return result.toString();
20567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
20577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // returns a string like toString but using the canonical character for most types,
20597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // e.g. M for M or L, E for E or c, y for y or U, etc. The hour field is canonicalized
20607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // to 'H' (for 24-hour types) or 'h' (for 12-hour types)
20617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String toCanonicalString() {
20627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            StringBuilder result = new StringBuilder();
20637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = 0; i < TYPE_LIMIT; ++i) {
20647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (original[i].length() != 0) {
20657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // append a string of the same length using the canonical character
20667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    for (int j = 0; j < types.length; ++j) {
20677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        int[] row = types[j];
20687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (row[1] == i) {
20697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            char originalChar = original[i].charAt(0);
20707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            char repeatChar = (originalChar=='h' || originalChar=='K')? 'h': (char)row[0];
20717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            result.append(Utility.repeat(String.valueOf(repeatChar), original[i].length()));
20727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            break;
20737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
20747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
20757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
20767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
20777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return result.toString();
20787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
20797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String getBasePattern() {
20817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            StringBuilder result = new StringBuilder();
20827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = 0; i < TYPE_LIMIT; ++i) {
20837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (baseOriginal[i].length() != 0) result.append(baseOriginal[i]);
20847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
20857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return result.toString();
20867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
20877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DateTimeMatcher set(String pattern, FormatParser fp, boolean allowDuplicateFields) {
20897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = 0; i < TYPE_LIMIT; ++i) {
20907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                type[i] = NONE;
20917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                original[i] = "";
20927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                baseOriginal[i] = "";
20937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
20947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fp.set(pattern);
20957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (Object obj : fp.getItems()) {
20967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (!(obj instanceof VariableField)) {
20977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    continue;
20987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
20997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                VariableField item = (VariableField)obj;
21007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String field = item.toString();
21017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (field.charAt(0) == 'a') continue; // skip day period, special case
21027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int canonicalIndex = item.getCanonicalIndex();
21037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //                if (canonicalIndex < 0) {
21047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //                    throw new IllegalArgumentException("Illegal field:\t"
21057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //                            + field + "\t in " + pattern);
21067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //                }
21077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int[] row = types[canonicalIndex];
21087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int typeValue = row[1];
21097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (original[typeValue].length() != 0) {
2110f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    if ( allowDuplicateFields ||
2111f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                          (original[typeValue].charAt(0) == 'r' && field.charAt(0) == 'U') ||
2112f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                          (original[typeValue].charAt(0) == 'U' && field.charAt(0) == 'r') ) {
21137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        continue;
21147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
21157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    throw new IllegalArgumentException("Conflicting fields:\t"
21167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            + original[typeValue] + ", " + field + "\t in " + pattern);
21177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
21187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                original[typeValue] = field;
21197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                char repeatChar = (char)row[0];
21207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int repeatCount = row[3];
21217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // #7930 removes hack to cap repeatCount at 3
21227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ("GEzvQ".indexOf(repeatChar) >= 0) repeatCount = 1;
21237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                baseOriginal[typeValue] = Utility.repeat(String.valueOf(repeatChar),repeatCount);
21247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int subTypeValue = row[2];
21257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (subTypeValue > 0) subTypeValue += field.length();
21267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                type[typeValue] = subTypeValue;
21277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
21287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return this;
21297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
21307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
21317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
21327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
21337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
21347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int getFieldMask() {
21357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int result = 0;
21367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = 0; i < type.length; ++i) {
21377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (type[i] != 0) result |= (1<<i);
21387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
21397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return result;
21407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
21417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
21427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
21437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
21447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
21457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @SuppressWarnings("unused")
21467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        void extractFrom(DateTimeMatcher source, int fieldMask) {
21477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = 0; i < type.length; ++i) {
21487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ((fieldMask & (1<<i)) != 0) {
21497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    type[i] = source.type[i];
21507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    original[i] = source.original[i];
21517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
21527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    type[i] = NONE;
21537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    original[i] = "";
21547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
21557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
21567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
21577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
21587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int getDistance(DateTimeMatcher other, int includeMask, DistanceInfo distanceInfo) {
21597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int result = 0;
21607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            distanceInfo.clear();
21617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = 0; i < type.length; ++i) {
21627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int myType = (includeMask & (1<<i)) == 0 ? 0 : type[i];
21637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int otherType = other.type[i];
21647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (myType == otherType) continue; // identical (maybe both zero) add 0
21657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (myType == 0) { // and other is not
21667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    result += EXTRA_FIELD;
21677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    distanceInfo.addExtra(i);
21687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (otherType == 0) { // and mine is not
21697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    result += MISSING_FIELD;
21707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    distanceInfo.addMissing(i);
21717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
21727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    result += Math.abs(myType - otherType); // square of mismatch
21737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
21747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
21757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return result;
21767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
21777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
21787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int compareTo(DateTimeMatcher that) {
21797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = 0; i < original.length; ++i) {
21807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int comp = original[i].compareTo(that.original[i]);
21817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (comp != 0) return -comp;
21827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
21837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return 0;
21847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
21857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
21867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean equals(Object other) {
21877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!(other instanceof DateTimeMatcher)) {
21887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return false;
21897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
21907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            DateTimeMatcher that = (DateTimeMatcher) other;
21917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = 0; i < original.length; ++i) {
21927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (!original[i].equals(that.original[i])) return false;
21937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
21947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return true;
21957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
21967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int hashCode() {
21977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int result = 0;
21987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = 0; i < original.length; ++i) {
21997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result ^= original[i].hashCode();
22007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
22017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return result;
22027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
22037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
22047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
22057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static class DistanceInfo {
22067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int missingFieldMask;
22077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int extraFieldMask;
22087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        void clear() {
22097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            missingFieldMask = extraFieldMask = 0;
22107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
22117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
22127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
22137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
22147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        void setTo(DistanceInfo other) {
22157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            missingFieldMask = other.missingFieldMask;
22167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            extraFieldMask = other.extraFieldMask;
22177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
22187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        void addMissing(int field) {
22197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            missingFieldMask |= (1<<field);
22207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
22217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        void addExtra(int field) {
22227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            extraFieldMask |= (1<<field);
22237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
22247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String toString() {
22257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return "missingFieldMask: " + DateTimePatternGenerator.showMask(missingFieldMask)
22267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            + ", extraFieldMask: " + DateTimePatternGenerator.showMask(extraFieldMask);
22277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
22287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
22297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
22307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert//eof
2231