All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.threeten.bp.format.SimpleDateTimeTextProvider Maven / Gradle / Ivy

Go to download

Backport of JSR-310 from JDK 8 to JDK 7 and JDK 6. NOT an implementation of the JSR.

There is a newer version: 1.7.0
Show newest version
/*
 * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 *  * Neither the name of JSR-310 nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.threeten.bp.format;

import static org.threeten.bp.temporal.ChronoField.AMPM_OF_DAY;
import static org.threeten.bp.temporal.ChronoField.DAY_OF_WEEK;
import static org.threeten.bp.temporal.ChronoField.ERA;
import static org.threeten.bp.temporal.ChronoField.MONTH_OF_YEAR;

import java.text.DateFormatSymbols;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.threeten.bp.temporal.IsoFields;
import org.threeten.bp.temporal.TemporalField;

/**
 * The Service Provider Implementation to obtain date-time text for a field.
 * 

* This implementation is based on extraction of data from a {@link DateFormatSymbols}. * *

Specification for implementors

* This class is immutable and thread-safe. */ final class SimpleDateTimeTextProvider extends DateTimeTextProvider { // TODO: Better implementation based on CLDR /** Comparator. */ private static final Comparator> COMPARATOR = new Comparator>() { @Override public int compare(Entry obj1, Entry obj2) { return obj2.getKey().length() - obj1.getKey().length(); // longest to shortest } }; /** Cache. */ private final ConcurrentMap, Object> cache = new ConcurrentHashMap, Object>(16, 0.75f, 2); //----------------------------------------------------------------------- @Override public String getText(TemporalField field, long value, TextStyle style, Locale locale) { Object store = findStore(field, locale); if (store instanceof LocaleStore) { return ((LocaleStore) store).getText(value, style); } return null; } @Override public Iterator> getTextIterator(TemporalField field, TextStyle style, Locale locale) { Object store = findStore(field, locale); if (store instanceof LocaleStore) { return ((LocaleStore) store).getTextIterator(style); } return null; } //----------------------------------------------------------------------- private Object findStore(TemporalField field, Locale locale) { Entry key = createEntry(field, locale); Object store = cache.get(key); if (store == null) { store = createStore(field, locale); cache.putIfAbsent(key, store); store = cache.get(key); } return store; } private Object createStore(TemporalField field, Locale locale) { if (field == MONTH_OF_YEAR) { DateFormatSymbols oldSymbols = DateFormatSymbols.getInstance(locale); Map> styleMap = new HashMap>(); Long f1 = 1L; Long f2 = 2L; Long f3 = 3L; Long f4 = 4L; Long f5 = 5L; Long f6 = 6L; Long f7 = 7L; Long f8 = 8L; Long f9 = 9L; Long f10 = 10L; Long f11 = 11L; Long f12 = 12L; String[] array = oldSymbols.getMonths(); Map map = new HashMap(); map.put(f1, array[Calendar.JANUARY]); map.put(f2, array[Calendar.FEBRUARY]); map.put(f3, array[Calendar.MARCH]); map.put(f4, array[Calendar.APRIL]); map.put(f5, array[Calendar.MAY]); map.put(f6, array[Calendar.JUNE]); map.put(f7, array[Calendar.JULY]); map.put(f8, array[Calendar.AUGUST]); map.put(f9, array[Calendar.SEPTEMBER]); map.put(f10, array[Calendar.OCTOBER]); map.put(f11, array[Calendar.NOVEMBER]); map.put(f12, array[Calendar.DECEMBER]); styleMap.put(TextStyle.FULL, map); map = new HashMap(); map.put(f1, narrowMonth(1, array[Calendar.JANUARY], locale)); map.put(f2, narrowMonth(2, array[Calendar.FEBRUARY], locale)); map.put(f3, narrowMonth(3, array[Calendar.MARCH], locale)); map.put(f4, narrowMonth(4, array[Calendar.APRIL], locale)); map.put(f5, narrowMonth(5, array[Calendar.MAY], locale)); map.put(f6, narrowMonth(6, array[Calendar.JUNE], locale)); map.put(f7, narrowMonth(7, array[Calendar.JULY], locale)); map.put(f8, narrowMonth(8, array[Calendar.AUGUST], locale)); map.put(f9, narrowMonth(9, array[Calendar.SEPTEMBER], locale)); map.put(f10, narrowMonth(10, array[Calendar.OCTOBER], locale)); map.put(f11, narrowMonth(11, array[Calendar.NOVEMBER], locale)); map.put(f12, narrowMonth(12, array[Calendar.DECEMBER], locale)); styleMap.put(TextStyle.NARROW, map); array = oldSymbols.getShortMonths(); map = new HashMap(); map.put(f1, array[Calendar.JANUARY]); map.put(f2, array[Calendar.FEBRUARY]); map.put(f3, array[Calendar.MARCH]); map.put(f4, array[Calendar.APRIL]); map.put(f5, array[Calendar.MAY]); map.put(f6, array[Calendar.JUNE]); map.put(f7, array[Calendar.JULY]); map.put(f8, array[Calendar.AUGUST]); map.put(f9, array[Calendar.SEPTEMBER]); map.put(f10, array[Calendar.OCTOBER]); map.put(f11, array[Calendar.NOVEMBER]); map.put(f12, array[Calendar.DECEMBER]); styleMap.put(TextStyle.SHORT, map); return createLocaleStore(styleMap); } if (field == DAY_OF_WEEK) { DateFormatSymbols oldSymbols = DateFormatSymbols.getInstance(locale); Map> styleMap = new HashMap>(); Long f1 = 1L; Long f2 = 2L; Long f3 = 3L; Long f4 = 4L; Long f5 = 5L; Long f6 = 6L; Long f7 = 7L; String[] array = oldSymbols.getWeekdays(); Map map = new HashMap(); map.put(f1, array[Calendar.MONDAY]); map.put(f2, array[Calendar.TUESDAY]); map.put(f3, array[Calendar.WEDNESDAY]); map.put(f4, array[Calendar.THURSDAY]); map.put(f5, array[Calendar.FRIDAY]); map.put(f6, array[Calendar.SATURDAY]); map.put(f7, array[Calendar.SUNDAY]); styleMap.put(TextStyle.FULL, map); map = new HashMap(); map.put(f1, narrowDayOfWeek(1, array[Calendar.MONDAY], locale)); map.put(f2, narrowDayOfWeek(2, array[Calendar.TUESDAY], locale)); map.put(f3, narrowDayOfWeek(3, array[Calendar.WEDNESDAY], locale)); map.put(f4, narrowDayOfWeek(4, array[Calendar.THURSDAY], locale)); map.put(f5, narrowDayOfWeek(5, array[Calendar.FRIDAY], locale)); map.put(f6, narrowDayOfWeek(6, array[Calendar.SATURDAY], locale)); map.put(f7, narrowDayOfWeek(7, array[Calendar.SUNDAY], locale)); styleMap.put(TextStyle.NARROW, map); array = oldSymbols.getShortWeekdays(); map = new HashMap(); map.put(f1, array[Calendar.MONDAY]); map.put(f2, array[Calendar.TUESDAY]); map.put(f3, array[Calendar.WEDNESDAY]); map.put(f4, array[Calendar.THURSDAY]); map.put(f5, array[Calendar.FRIDAY]); map.put(f6, array[Calendar.SATURDAY]); map.put(f7, array[Calendar.SUNDAY]); styleMap.put(TextStyle.SHORT, map); return createLocaleStore(styleMap); } if (field == AMPM_OF_DAY) { DateFormatSymbols oldSymbols = DateFormatSymbols.getInstance(locale); Map> styleMap = new HashMap>(); String[] array = oldSymbols.getAmPmStrings(); Map map = new HashMap(); map.put(0L, array[Calendar.AM]); map.put(1L, array[Calendar.PM]); styleMap.put(TextStyle.FULL, map); styleMap.put(TextStyle.SHORT, map); // re-use, as we don't have different data return createLocaleStore(styleMap); } if (field == ERA) { DateFormatSymbols oldSymbols = DateFormatSymbols.getInstance(locale); Map> styleMap = new HashMap>(); String[] array = oldSymbols.getEras(); Map map = new HashMap(); map.put(0L, array[GregorianCalendar.BC]); map.put(1L, array[GregorianCalendar.AD]); styleMap.put(TextStyle.SHORT, map); if (locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) { map = new HashMap(); map.put(0L, "Before Christ"); map.put(1L, "Anno Domini"); styleMap.put(TextStyle.FULL, map); } else { // re-use, as we don't have different data styleMap.put(TextStyle.FULL, map); } map = new HashMap(); map.put(0L, array[GregorianCalendar.BC].substring(0, 1)); map.put(1L, array[GregorianCalendar.AD].substring(0, 1)); styleMap.put(TextStyle.NARROW, map); return createLocaleStore(styleMap); } // hard code English quarter text if (field == IsoFields.QUARTER_OF_YEAR) { Map> styleMap = new HashMap>(); Map map = new HashMap(); map.put(1L, "Q1"); map.put(2L, "Q2"); map.put(3L, "Q3"); map.put(4L, "Q4"); styleMap.put(TextStyle.SHORT, map); map = new HashMap(); map.put(1L, "1st quarter"); map.put(2L, "2nd quarter"); map.put(3L, "3rd quarter"); map.put(4L, "4th quarter"); styleMap.put(TextStyle.FULL, map); return createLocaleStore(styleMap); } return ""; // null marker for map } // for China/Japan we need special behaviour private String narrowMonth(int month, String text, Locale locale) { if (locale.getLanguage().equals("zh") && locale.getCountry().equals("CN")) { switch (month) { case 1: return "\u4e00"; case 2: return "\u4e8c"; case 3: return "\u4e09"; case 4: return "\u56db"; case 5: return "\u4e94"; case 6: return "\u516d"; case 7: return "\u4e03"; case 8: return "\u516b"; case 9: return "\u4e5d"; case 10: return "\u5341"; case 11: return "\u5341\u4e00"; case 12: return "\u5341\u4e8c"; } } if (locale.getLanguage().equals("ar")) { switch (month) { case 1: return "\u064a"; case 2: return "\u0641"; case 3: return "\u0645"; case 4: return "\u0623"; case 5: return "\u0648"; case 6: return "\u0646"; case 7: return "\u0644"; case 8: return "\u063a"; case 9: return "\u0633"; case 10: return "\u0643"; case 11: return "\u0628"; case 12: return "\u062f"; } } if (locale.getLanguage().equals("ja") && locale.getCountry().equals("JP")) { return Integer.toString(month); } return text.substring(0, 1); } // for China we need to select the last character private String narrowDayOfWeek(int dow, String text, Locale locale) { if (locale.getLanguage().equals("zh") && locale.getCountry().equals("CN")) { switch (dow) { case 1: return "\u4e00"; case 2: return "\u4e8c"; case 3: return "\u4e09"; case 4: return "\u56db"; case 5: return "\u4e94"; case 6: return "\u516d"; case 7: return "\u65e5"; } } if (locale.getLanguage().equals("ar")) { switch (dow) { case 1: return "\u0646"; case 2: return "\u062b"; case 3: return "\u0631"; case 4: return "\u062e"; case 5: return "\u062c"; case 6: return "\u0633"; case 7: return "\u062d"; } } return text.substring(0, 1); } //----------------------------------------------------------------------- /** * Helper method to create an immutable entry. * * @param text the text, not null * @param field the field, not null * @return the entry, not null */ private static Entry createEntry(A text, B field) { return new SimpleImmutableEntry(text, field); } //----------------------------------------------------------------------- private static LocaleStore createLocaleStore(Map> valueTextMap) { valueTextMap.put(TextStyle.FULL_STANDALONE, valueTextMap.get(TextStyle.FULL)); valueTextMap.put(TextStyle.SHORT_STANDALONE, valueTextMap.get(TextStyle.SHORT)); if (valueTextMap.containsKey(TextStyle.NARROW) && valueTextMap.containsKey(TextStyle.NARROW_STANDALONE) == false) { valueTextMap.put(TextStyle.NARROW_STANDALONE, valueTextMap.get(TextStyle.NARROW)); } return new LocaleStore(valueTextMap); } /** * Stores the text for a single locale. *

* Some fields have a textual representation, such as day-of-week or month-of-year. * These textual representations can be captured in this class for printing * and parsing. *

* This class is immutable and thread-safe. */ static final class LocaleStore { /** * Map of value to text. */ private final Map> valueTextMap; /** * Parsable data. */ private final Map>> parsable; //----------------------------------------------------------------------- /** * Constructor. * * @param valueTextMap the map of values to text to store, assigned and not altered, not null */ LocaleStore(Map> valueTextMap) { this.valueTextMap = valueTextMap; Map>> map = new HashMap>>(); List> allList = new ArrayList>(); for (TextStyle style : valueTextMap.keySet()) { Map> reverse = new HashMap>(); for (Map.Entry entry : valueTextMap.get(style).entrySet()) { if (reverse.put(entry.getValue(), createEntry(entry.getValue(), entry.getKey())) != null) { continue; // not parsable, try next style } } List> list = new ArrayList>(reverse.values()); Collections.sort(list, COMPARATOR); map.put(style, list); allList.addAll(list); map.put(null, allList); } Collections.sort(allList, COMPARATOR); this.parsable = map; } //----------------------------------------------------------------------- /** * Gets the text for the specified field value, locale and style * for the purpose of printing. * * @param value the value to get text for, not null * @param style the style to get text for, not null * @return the text for the field value, null if no text found */ String getText(long value, TextStyle style) { Map map = valueTextMap.get(style); return map != null ? map.get(value) : null; } /** * Gets an iterator of text to field for the specified style for the purpose of parsing. *

* The iterator must be returned in order from the longest text to the shortest. * * @param style the style to get text for, null for all parsable text * @return the iterator of text to field pairs, in order from longest text to shortest text, * null if the style is not parsable */ Iterator> getTextIterator(TextStyle style) { List> list = parsable.get(style); return list != null ? list.iterator() : null; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy