From 565b33d0dae8105d9aac3790dcd73870d7463ba5 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Fri, 25 Apr 2025 15:34:32 -0500 Subject: [PATCH 1/8] CLDR-17851 SupplementalCalendarData - minimal, modern parser - minimal test case - commented-out 'dump' function --- .../org/unicode/cldr/icu/LDMLConstants.java | 7 +- .../cldr/util/SupplementalCalendarData.java | 182 ++++++++++++++++++ .../cldr/util/SupplementalDataInfo.java | 11 ++ .../cldr/util/TestSupplementalDataInfo.java | 30 +++ 4 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalCalendarData.java diff --git a/tools/cldr-code/src/main/java/org/unicode/cldr/icu/LDMLConstants.java b/tools/cldr-code/src/main/java/org/unicode/cldr/icu/LDMLConstants.java index 6fd6c7a0196..f6a5780fe32 100644 --- a/tools/cldr-code/src/main/java/org/unicode/cldr/icu/LDMLConstants.java +++ b/tools/cldr-code/src/main/java/org/unicode/cldr/icu/LDMLConstants.java @@ -258,8 +258,13 @@ public class LDMLConstants { public static final String SEGMENTATIONS = "segmentations"; public static final String TIMEZONE_DATA = "timezoneData"; public static final String CALENDAR_DATA = "calendarData"; - public static final String CALENDAR_PREFERENCE_DATA = "calendarPreferenceData"; public static final String CALENDAR_PREFERENCE = "calendarPreference"; + public static final String CALENDAR_PREFERENCE_DATA = "calendarPreferenceData"; + public static final String CALENDAR_SYSTEM = "calendarSystem"; + public static final String INHERIT_ERAS = "inheritEras"; + public static final String CODE = "code"; + public static final String END = "end"; + public static final String START = "start"; public static final String ORDERING = "ordering"; public static final String WEEK_DATA = "weekData"; public static final String MEASUREMENT_DATA = "measurementData"; diff --git a/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalCalendarData.java b/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalCalendarData.java new file mode 100644 index 00000000000..2ab61829112 --- /dev/null +++ b/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalCalendarData.java @@ -0,0 +1,182 @@ +package org.unicode.cldr.util; + +import com.ibm.icu.impl.locale.XCldrStub.ImmutableMap; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Supplier; +import org.unicode.cldr.icu.LDMLConstants; + +public class SupplementalCalendarData implements Iterable { + /** an element */ + static class EraData { + static final int INDEX = 4; // index of our element + + /** the xpath */ + private final XPathValue xpath; + + EraData(final XPathValue xpath) { + this.xpath = xpath; + // TODO: validate (in unit test) + } + + public int getType() { + return Integer.parseInt(xpath.getAttributeValue(INDEX, LDMLConstants.TYPE)); + } + + public String getStart() { + return xpath.getAttributeValue(INDEX, LDMLConstants.START); + } + + public String getEnd() { + return xpath.getAttributeValue(INDEX, LDMLConstants.END); + } + + public String getCode() { + return xpath.getAttributeValue(INDEX, LDMLConstants.CODE); + } + + public String[] getAliases() { + final String s = xpath.getAttributeValue(INDEX, LDMLConstants.ALIASES); + if (s == null) return "".split(" "); // empty array + return s.split(" "); + } + + @Override + public String toString() { + return String.format( + "ERA %d, [%s-%s] code=%s, aliases=%s", + getType(), getStart(), getEnd(), getCode(), String.join(",", getAliases())); + } + } + + /** a element */ + static class CalendarData implements Iterable { + static final int INDEX = 3; // index of our element + + /** the xpath */ + private XPathValue system = null; + + /** the xpath */ + private XPathValue inheritEras = null; + + private Map eras = new HashMap(); + + public String getSystemType() { + if (system == null) return null; + return system.getAttributeValue(INDEX, LDMLConstants.TYPE); + } + + public String getInheritEras() { + if (inheritEras == null) return null; + return inheritEras.getAttributeValue(INDEX, LDMLConstants.CALENDAR); + } + + @Override + public Iterator iterator() { + return eras.keySet().iterator(); + } + + public EraData get(Integer era) { + return eras.get(era); + } + } + + private Map typeToCalendar = new HashMap<>(); + + static class Parser implements Consumer, Supplier { + private Map typeToCalendar = new HashMap<>(); + + CalendarData getCalendar(final String type) { + return typeToCalendar.computeIfAbsent(type, ignored -> new CalendarData()); + } + + @Override + public void accept(final XPathValue x) { + // assert that we are in //supplementalData/calendarData/calendar… + if (!x.getElement(2).equals(LDMLConstants.CALENDAR)) { + throw new UnsupportedOperationException("Unsupported supplemental xpath " + x); + } + if (x.size() < 4) return; // top level or + final String calendarType = x.getAttributeValue(2, LDMLConstants.TYPE); + if (calendarType == null || calendarType.isEmpty()) { + throw new IllegalArgumentException("No calendar type on " + x); + } + final CalendarData c = getCalendar(calendarType); + + final String level3 = x.getElement(3); + switch (level3) { + case LDMLConstants.CALENDAR_SYSTEM: + acceptCalendarSystem(x, c); + break; + case LDMLConstants.INHERIT_ERAS: + acceptInheritEras(x, c); + break; + case LDMLConstants.ERAS: + acceptEras(x, c); + break; + default: + throw new IllegalArgumentException( + "Unsupported supplemental xpath " + x + " got " + level3); + } + } + + private void acceptEras(XPathValue x, CalendarData c) { + if (x.size() < 5) { + return; // top level element + } else if (x.size() > 5) { + // something unexpectedly deep + throw new IllegalArgumentException("Unsupported deep era xpath " + x); + } + final String e = x.getElement(4); + if (!e.equals(LDMLConstants.ERA)) { + throw new IllegalArgumentException("Unsupported era xpath " + x); + } + final String type = x.getAttributeValue(4, LDMLConstants.TYPE); + if (type == null || type.isEmpty()) { + throw new IllegalArgumentException("Bad calendar era, no biscuit " + x); + } + final Integer n = Integer.parseInt(type); + if (c.eras.putIfAbsent(n, new EraData(x)) != null) { + throw new IllegalArgumentException("Duplicate calendar era " + x); + } + } + + private void acceptInheritEras(XPathValue x, CalendarData c) { + if (c.inheritEras != null) { + throw new IllegalArgumentException("Duplicate calendar inheritEras: " + x); + } + c.inheritEras = x; + } + + private void acceptCalendarSystem(XPathValue x, CalendarData c) { + if (c.system != null) { + throw new IllegalArgumentException("Duplicate calendar system: " + x); + } + c.system = x; + } + + @Override + public SupplementalCalendarData get() { + return new SupplementalCalendarData(typeToCalendar); + } + } + + private SupplementalCalendarData(Map m) { + // TODO: freeze all types + // m.forEach(c -> c.freeze()); + this.typeToCalendar = ImmutableMap.copyOf(m); + } + + /** Calendar data for this type */ + public CalendarData get(final String t) { + return typeToCalendar.get(t); + } + + /** iterate over types */ + @Override + public Iterator iterator() { + return typeToCalendar.keySet().iterator(); + } +} diff --git a/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalDataInfo.java b/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalDataInfo.java index 3cdc60f6577..bac10114482 100644 --- a/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalDataInfo.java +++ b/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalDataInfo.java @@ -65,6 +65,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import org.unicode.cldr.icu.LDMLConstants; import org.unicode.cldr.test.CoverageLevel2; import org.unicode.cldr.tool.LikelySubtags; import org.unicode.cldr.tool.SubdivisionNames; @@ -1447,6 +1448,9 @@ public void handlePathValue(String path, String value) { if (handleTerritoryInfo(parts)) { return; } + } else if (level1.equals(LDMLConstants.CALENDAR_DATA)) { + calendarDataParser.accept(parts); + return; } else if (level1.equals("calendarPreferenceData")) { handleCalendarPreferenceData(parts); return; @@ -2555,6 +2559,13 @@ public int parseIntegerOrNull(String attributeValue) { private Set CLDRScriptCodes; + private final SupplementalCalendarData.Parser calendarDataParser = + new SupplementalCalendarData.Parser(); + + public SupplementalCalendarData getCalendarData() { + return calendarDataParser.get(); + } + /** * Get the population data for a language. Warning: if the language has script variants, cycle * on those variants. diff --git a/tools/cldr-code/src/test/java/org/unicode/cldr/util/TestSupplementalDataInfo.java b/tools/cldr-code/src/test/java/org/unicode/cldr/util/TestSupplementalDataInfo.java index 0a729c52fb8..65d2b13a7e6 100644 --- a/tools/cldr-code/src/test/java/org/unicode/cldr/util/TestSupplementalDataInfo.java +++ b/tools/cldr-code/src/test/java/org/unicode/cldr/util/TestSupplementalDataInfo.java @@ -90,4 +90,34 @@ void TestParentLocales() { } } } + + @Test + void TestCalendarData() { + final SupplementalDataInfo sdi = CLDRConfig.getInstance().getSupplementalDataInfo(); + + SupplementalCalendarData cal = sdi.getCalendarData(); + + assertNotNull(cal); + + // dumpCalendarData(cal); + assertNotNull(cal.get("gregorian")); + assertNotNull(cal.get("japanese")); + assertEquals("solar", cal.get("gregorian").getSystemType()); + assertEquals("717-11-17", cal.get("japanese").get(8).getStart()); + } + + // private void dumpCalendarData(SupplementalCalendarData cal) { + // for(final String calType : cal) { + // System.out.println("Calendar: " + calType); + // final CalendarData c = cal.get(calType); + // assertNotNull(c, calType); + // System.out.println(" system : " + c.getSystemType()); + // System.out.println(" inherit: " + c.getInheritEras()); + // for (final Integer n : c) { + // System.out.println("* " + n); + // final EraData e = c.get(n); + // System.out.println(" " + e); + // } + // } + // } } From da0ac32954ad6bb58f621771f5de3e2c06b1a9f6 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Fri, 25 Apr 2025 15:59:56 -0500 Subject: [PATCH 2/8] CLDR-17851 SupplementalCalendarData - update example generator to use --- .../unicode/cldr/test/ExampleGenerator.java | 114 ++++++++---------- .../cldr/util/SupplementalCalendarData.java | 4 +- .../cldr/unittest/TestExampleGenerator.java | 2 +- 3 files changed, 51 insertions(+), 69 deletions(-) diff --git a/tools/cldr-code/src/main/java/org/unicode/cldr/test/ExampleGenerator.java b/tools/cldr-code/src/main/java/org/unicode/cldr/test/ExampleGenerator.java index 11db68ac39b..4a6e95c2897 100644 --- a/tools/cldr-code/src/main/java/org/unicode/cldr/test/ExampleGenerator.java +++ b/tools/cldr-code/src/main/java/org/unicode/cldr/test/ExampleGenerator.java @@ -81,6 +81,7 @@ import org.unicode.cldr.util.Rational.FormatStyle; import org.unicode.cldr.util.ScriptToExemplars; import org.unicode.cldr.util.SimpleUnicodeSetFormatter; +import org.unicode.cldr.util.SupplementalCalendarData; import org.unicode.cldr.util.SupplementalDataInfo; import org.unicode.cldr.util.SupplementalDataInfo.CurrencyNumberInfo; import org.unicode.cldr.util.SupplementalDataInfo.PluralInfo; @@ -293,58 +294,6 @@ public void setCachingEnabled(boolean enabled) { HelpMessages helpMessages; - /* For each calendar type, maps the closest two eras to 2025 - * defined in that calendar to their corresponding start/end date. - * Dates are adjusted to be 2 days after official era start date and - * 2 days before era end date to avoid time zone issues. - * TODO: include methods for calendarData in supplementalDataInfo API - * to extract this data directly from supplementaldata.xml - */ - public static final Map> CALENDAR_ERAS = - new HashMap<>() { - { // month 0-indexed. start/end days adjusted by +/- 2, respectively - put( - "gregorian", - List.of( - new GregorianCalendar(0, 11, 29).getTime(), - new GregorianCalendar(1, 0, 03).getTime())); - put( - "japanese", - List.of( - new GregorianCalendar(1989, 0, 10).getTime(), - new GregorianCalendar(2019, 4, 3).getTime())); - put( - "islamic", - List.of( - new GregorianCalendar(622, 6, 17).getTime(), - new GregorianCalendar(622, 6, 12).getTime())); - put("chinese", List.of(new GregorianCalendar(-2636, 0, 03).getTime())); - put("hebrew", List.of(new GregorianCalendar(-3760, 9, 9).getTime())); - put("buddhist", List.of(new GregorianCalendar(-542, 0, 03).getTime())); - put( - "coptic", - List.of( - new GregorianCalendar(284, 07, 26).getTime(), - new GregorianCalendar(284, 07, 31).getTime())); - put("persian", List.of(new GregorianCalendar(622, 0, 03).getTime())); - put("dangi", List.of(new GregorianCalendar(-2332, 0, 03).getTime())); - put( - "ethiopic", - List.of( - new GregorianCalendar(8, 07, 26).getTime(), - new GregorianCalendar(8, 07, 31).getTime())); - put( - "ethiopic-amete-alem", - List.of(new GregorianCalendar(-5492, 07, 27).getTime())); - put("indian", List.of(new GregorianCalendar(79, 0, 03).getTime())); - put( - "roc", - List.of( - new GregorianCalendar(1911, 11, 29).getTime(), - new GregorianCalendar(1912, 0, 03).getTime())); - } - }; - // map relativeTimePattern counts to possible numeric examples. // For few , many, and other there is not a single number that is in that category for // all locales, so we provide a list of values that might be good examples and use the @@ -3151,10 +3100,7 @@ private void handleDateRangePattern(String value, List examples) { setBackground(dateFormat.format(DATE_SAMPLE2)))); } - /** - * Add examples for eras. First checks if there is info for this calendar type and this era type - * in the CALENDAR_ERAS map, then generates a sample date based on this info and formats it - */ + /** Add examples for eras. Generates a sample date based on this info and formats it */ private void handleEras(XPathParts parts, String value, List examples) { String calendarId = parts.getAttributeValue(3, "type"); String type = parts.getAttributeValue(-1, "type"); @@ -3162,19 +3108,37 @@ private void handleEras(XPathParts parts, String value, List examples) { (calendarId.startsWith("islamic")) ? "islamic" : calendarId; // islamic variations map to same sample - if (!CALENDAR_ERAS.containsKey(id)) { - return; + final SupplementalCalendarData.CalendarData calendarData = + supplementalDataInfo.getCalendarData().get(id); + + if (calendarData == null) { + throw new IllegalArgumentException("Could not load supplementalCalendarData for " + id); } - int typeIndex = Integer.parseInt(type); - if (calendarId.equals("japanese")) { - if (typeIndex < 235) { // examples only for 2 most recent eras - return; - } else { - typeIndex %= 235; // map to length 2 list + final int typeIndex = Integer.parseInt(type); + + final SupplementalCalendarData.EraData eraData = calendarData.get(typeIndex); + if (eraData == null) { + return; // no era data + } + GregorianCalendar startCal = forDateString(eraData.getStart()); + GregorianCalendar endCal = forDateString(eraData.getEnd()); + + if (startCal == null && endCal == null) return; // no data + + if (startCal != null && endCal != null) { + // go 2 days before end + endCal.roll(Calendar.JULIAN_DAY, -2); + if (endCal.before(startCal)) { + endCal = startCal; } + } else if (startCal == null) { + endCal.roll(Calendar.JULIAN_DAY, -2); + } else if (endCal == null) { + endCal = forDateString("2002-07-15"); // CLDR repo root commit } - List eraDates = CALENDAR_ERAS.get(id); - Date sample = eraDates.get(typeIndex); + + final Date sample = endCal.getTime(); + String skeleton = "Gy"; String checkPath = "//ldml/dates/calendars/calendar[@type=\"" @@ -3192,6 +3156,24 @@ private void handleEras(XPathParts parts, String value, List examples) { examples.add(sdf.format(sample)); } + private GregorianCalendar forDateString(String ymd) { + if (ymd == null) return null; + int multiplier = 1; + if (ymd.startsWith("-")) { + multiplier = -1; + ymd = ymd.substring(1); + } + final String[] parts = ymd.split("-"); + try { + return new GregorianCalendar( + multiplier * Integer.parseInt(parts[0]), + Integer.parseInt(parts[1]) - 1, + Integer.parseInt(parts[2])); + } catch (NumberFormatException nfe) { + throw new IllegalArgumentException("While parsing date string " + ymd, nfe); + } + } + /** * Add examples for quarters for the gregorian calendar, matching each quarter type (1, 2, 3, 4) * to a corresponding sample month and formatting an example with that date diff --git a/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalCalendarData.java b/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalCalendarData.java index 2ab61829112..4cbf70f93b4 100644 --- a/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalCalendarData.java +++ b/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalCalendarData.java @@ -10,7 +10,7 @@ public class SupplementalCalendarData implements Iterable { /** an element */ - static class EraData { + public static class EraData { static final int INDEX = 4; // index of our element /** the xpath */ @@ -52,7 +52,7 @@ public String toString() { } /** a element */ - static class CalendarData implements Iterable { + public static class CalendarData implements Iterable { static final int INDEX = 3; // index of our element /** the xpath */ diff --git a/tools/cldr-code/src/test/java/org/unicode/cldr/unittest/TestExampleGenerator.java b/tools/cldr-code/src/test/java/org/unicode/cldr/unittest/TestExampleGenerator.java index d643c485fac..2ecec1d4e87 100644 --- a/tools/cldr-code/src/test/java/org/unicode/cldr/unittest/TestExampleGenerator.java +++ b/tools/cldr-code/src/test/java/org/unicode/cldr/unittest/TestExampleGenerator.java @@ -1961,7 +1961,7 @@ public VoterInfo getVoterInfo() { continue; } Level level = SDI.getCoverageLevel(xpath, "en"); - if (level.compareTo(Level.COMPREHENSIVE) == 0) { + if (level.isAtLeast(Level.COMPREHENSIVE)) { continue; } String starred = ps.set(xpath); From 3038d1424a0bc6d0e7baeffc168a11dd21bf6007 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Fri, 25 Apr 2025 16:12:02 -0500 Subject: [PATCH 3/8] CLDR-17851 Supplemental Data test fixup - move TestEraMap into TestSDI --- .../cldr/unittest/TestExampleGenerator.java | 30 ------------------- .../cldr/util/TestSupplementalDataInfo.java | 30 +++++++++++++++++++ 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/tools/cldr-code/src/test/java/org/unicode/cldr/unittest/TestExampleGenerator.java b/tools/cldr-code/src/test/java/org/unicode/cldr/unittest/TestExampleGenerator.java index 2ecec1d4e87..c1def428818 100644 --- a/tools/cldr-code/src/test/java/org/unicode/cldr/unittest/TestExampleGenerator.java +++ b/tools/cldr-code/src/test/java/org/unicode/cldr/unittest/TestExampleGenerator.java @@ -9,12 +9,10 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; import com.google.common.collect.TreeMultimap; -import com.ibm.icu.impl.Relation; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -1730,34 +1728,6 @@ public void TestUnicodeSetExamples() { } } - public void TestEraMap() { - ExampleGenerator exampleGenerator = getExampleGenerator("en"); - Relation keyToSubtypes = SupplementalDataInfo.getInstance().getBcp47Keys(); - Set calendars = keyToSubtypes.get("ca"); // gets calendar codes - Map codeToType = - new HashMap<>() { - { // calendars where code != type - put("gregory", "gregorian"); - put("iso8601", "gregorian"); - put("ethioaa", "ethiopic-amete-alem"); - put("islamic-civil", "islamic"); - put("islamic-rgsa", "islamic"); - put("islamic-tbla", "islamic"); - put("islamic-umalqura", "islamic"); - put("islamicc", "islamic"); - } - }; - for (String id : calendars) { - if (codeToType.containsKey(id)) { - id = codeToType.get(id); - } - Map> calendarMap = ExampleGenerator.CALENDAR_ERAS; - assertTrue( - "CALENDAR_ERAS map contains calendar type \"" + id + "\"", - calendarMap.containsKey(id)); - } - } - public void TestEraFormats() { ExampleGenerator exampleGeneratorJa = getExampleGenerator("ja"); ExampleGenerator exampleGeneratorEs = getExampleGenerator("es"); diff --git a/tools/cldr-code/src/test/java/org/unicode/cldr/util/TestSupplementalDataInfo.java b/tools/cldr-code/src/test/java/org/unicode/cldr/util/TestSupplementalDataInfo.java index 65d2b13a7e6..17a2ed01a0c 100644 --- a/tools/cldr-code/src/test/java/org/unicode/cldr/util/TestSupplementalDataInfo.java +++ b/tools/cldr-code/src/test/java/org/unicode/cldr/util/TestSupplementalDataInfo.java @@ -4,8 +4,12 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import com.ibm.icu.impl.Relation; import com.ibm.icu.text.PluralRules; import com.ibm.icu.util.ULocale; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; import org.junit.jupiter.api.Test; import org.unicode.cldr.util.SupplementalDataInfo.ApprovalRequirementMatcher; import org.unicode.cldr.util.SupplementalDataInfo.ParentLocaleComponent; @@ -104,6 +108,32 @@ void TestCalendarData() { assertNotNull(cal.get("japanese")); assertEquals("solar", cal.get("gregorian").getSystemType()); assertEquals("717-11-17", cal.get("japanese").get(8).getStart()); + + // moved from TestExampleGenerator.TestEraMap + Relation keyToSubtypes = SupplementalDataInfo.getInstance().getBcp47Keys(); + Set calendars = keyToSubtypes.get("ca"); // gets calendar codes + Map codeToType = + new HashMap<>() { + { // calendars where code != type + put("gregory", "gregorian"); + put("iso8601", "gregorian"); + put("ethioaa", "ethiopic-amete-alem"); + put("islamic-civil", "islamic"); + put("islamic-rgsa", "islamic"); + put("islamic-tbla", "islamic"); + put("islamic-umalqura", "islamic"); + put("islamicc", "islamic"); + } + }; + for (String id : calendars) { + if (codeToType.containsKey(id)) { + id = codeToType.get(id); + } + final String calId = id; + assertNotNull( + cal.get(calId), + () -> "calendar supplemental data missing calendar type \"" + calId + "\""); + } } // private void dumpCalendarData(SupplementalCalendarData cal) { From 398b9cbb4ac499d7dc5d7489acb93896effba512 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Fri, 25 Apr 2025 17:32:52 -0500 Subject: [PATCH 4/8] CLDR-17851 update ExampleGenerator and tests - use new supplemental data info --- .../unicode/cldr/test/ExampleGenerator.java | 55 ++++++++++++++----- .../cldr/unittest/TestExampleGenerator.java | 4 +- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/tools/cldr-code/src/main/java/org/unicode/cldr/test/ExampleGenerator.java b/tools/cldr-code/src/main/java/org/unicode/cldr/test/ExampleGenerator.java index 4a6e95c2897..220c5a6e6dc 100644 --- a/tools/cldr-code/src/main/java/org/unicode/cldr/test/ExampleGenerator.java +++ b/tools/cldr-code/src/main/java/org/unicode/cldr/test/ExampleGenerator.java @@ -3104,10 +3104,10 @@ private void handleDateRangePattern(String value, List examples) { private void handleEras(XPathParts parts, String value, List examples) { String calendarId = parts.getAttributeValue(3, "type"); String type = parts.getAttributeValue(-1, "type"); - String id = - (calendarId.startsWith("islamic")) - ? "islamic" - : calendarId; // islamic variations map to same sample + String id = calendarId; + if (id.equals("generic") || id.equals("iso8601")) { + id = "gregorian"; // use Gregorian eras, 'generic' is not in the data + } final SupplementalCalendarData.CalendarData calendarData = supplementalDataInfo.getCalendarData().get(id); @@ -3123,21 +3123,48 @@ private void handleEras(XPathParts parts, String value, List examples) { GregorianCalendar startCal = forDateString(eraData.getStart()); GregorianCalendar endCal = forDateString(eraData.getEnd()); - if (startCal == null && endCal == null) return; // no data + final SupplementalCalendarData.EraData prevEra = calendarData.get(typeIndex - 1); + final SupplementalCalendarData.EraData nextEra = calendarData.get(typeIndex + 1); + + if (startCal == null && prevEra != null && prevEra.getEnd() != null) { + startCal = forDateString(prevEra.getEnd()); + // shift forward so we are in the next era + startCal.setTimeInMillis(startCal.getTimeInMillis() + (DateConstants.MILLIS_PER_DAY)); + } + if (endCal == null && nextEra != null && nextEra.getStart() != null) { + endCal = forDateString(nextEra.getStart()); + // shift backward so we are in the prev era + endCal.setTimeInMillis(endCal.getTimeInMillis() - (DateConstants.MILLIS_PER_DAY)); + } + + GregorianCalendar sampleDate = null; if (startCal != null && endCal != null) { - // go 2 days before end - endCal.roll(Calendar.JULIAN_DAY, -2); - if (endCal.before(startCal)) { - endCal = startCal; + // roll back a day to not hit the edge + sampleDate = endCal; + sampleDate.setTimeInMillis( + sampleDate.getTimeInMillis() - (DateConstants.MILLIS_PER_DAY)); + } else if (startCal == null && endCal != null) { + // roll back a day to not hit the edge + sampleDate = endCal; + sampleDate.setTimeInMillis( + sampleDate.getTimeInMillis() - (DateConstants.MILLIS_PER_DAY)); + } else if (startCal != null && endCal == null) { + sampleDate = forDateString("2002-07-15"); // CLDR repo root commit + if (sampleDate.before(startCal)) { + sampleDate = startCal; + sampleDate.setTimeInMillis( + sampleDate.getTimeInMillis() + (DateConstants.MILLIS_PER_DAY)); } - } else if (startCal == null) { - endCal.roll(Calendar.JULIAN_DAY, -2); - } else if (endCal == null) { - endCal = forDateString("2002-07-15"); // CLDR repo root commit + } else { + // System.err.println("No good date for " + eraData); + // TODO: should be an error in TestSupplementalDataInfo + sampleDate = null; } - final Date sample = endCal.getTime(); + if (sampleDate == null) return; // could not find the time + + final Date sample = sampleDate.getTime(); String skeleton = "Gy"; String checkPath = diff --git a/tools/cldr-code/src/test/java/org/unicode/cldr/unittest/TestExampleGenerator.java b/tools/cldr-code/src/test/java/org/unicode/cldr/unittest/TestExampleGenerator.java index c1def428818..758cf4c9000 100644 --- a/tools/cldr-code/src/test/java/org/unicode/cldr/unittest/TestExampleGenerator.java +++ b/tools/cldr-code/src/test/java/org/unicode/cldr/unittest/TestExampleGenerator.java @@ -1734,7 +1734,7 @@ public void TestEraFormats() { ExampleGenerator exampleGeneratorZh = getExampleGenerator("zh"); checkValue( "japanese type=235 abbreviated", - "〖平成1年〗", + "〖平成31年〗", exampleGeneratorJa, "//ldml/dates/calendars/calendar[@type=\"japanese\"]/eras/eraAbbr/era[@type=\"235\"]"); checkValue( @@ -1749,7 +1749,7 @@ public void TestEraFormats() { "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/eras/eraNames/era[@type=\"0\"][@alt=\"variant\"]"); checkValue( "roc type=1 abbreviated", - "〖民国1年〗", + "〖民国91年〗", exampleGeneratorZh, "//ldml/dates/calendars/calendar[@type=\"roc\"]/eras/eraAbbr/era[@type=\"1\"]"); } From a20ea88a9b84fc9be0a334aae0f7c2c18cdc9f6e Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Fri, 25 Apr 2025 17:56:08 -0500 Subject: [PATCH 5/8] CLDR-17851 fix for BH era MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BH, added in CLDR-18464, is out of order… attempt to find the prev and next era by date. --- .../unicode/cldr/test/ExampleGenerator.java | 47 +++++++++--------- .../cldr/util/SupplementalCalendarData.java | 49 ++++++++++++++++++- 2 files changed, 70 insertions(+), 26 deletions(-) diff --git a/tools/cldr-code/src/main/java/org/unicode/cldr/test/ExampleGenerator.java b/tools/cldr-code/src/main/java/org/unicode/cldr/test/ExampleGenerator.java index 220c5a6e6dc..ebef35be7cc 100644 --- a/tools/cldr-code/src/main/java/org/unicode/cldr/test/ExampleGenerator.java +++ b/tools/cldr-code/src/main/java/org/unicode/cldr/test/ExampleGenerator.java @@ -3120,19 +3120,34 @@ private void handleEras(XPathParts parts, String value, List examples) { if (eraData == null) { return; // no era data } - GregorianCalendar startCal = forDateString(eraData.getStart()); - GregorianCalendar endCal = forDateString(eraData.getEnd()); + GregorianCalendar startCal = eraData.getStartCalendar(); + GregorianCalendar endCal = eraData.getEndCalendar(); - final SupplementalCalendarData.EraData prevEra = calendarData.get(typeIndex - 1); - final SupplementalCalendarData.EraData nextEra = calendarData.get(typeIndex + 1); + final SupplementalCalendarData.EraData eminusone = calendarData.get(typeIndex - 1); + final SupplementalCalendarData.EraData eplusone = calendarData.get(typeIndex + 1); + + SupplementalCalendarData.EraData prevEra = null; + SupplementalCalendarData.EraData nextEra = null; + + // see if we can find the 'prev' and 'next' era by date + if (eminusone != null && eminusone.compareTo(eraData) < 0) { + prevEra = eminusone; + } else if (eplusone != null && eplusone.compareTo(eraData) < 0) { + prevEra = eplusone; + } + if (eminusone != null && eminusone.compareTo(eraData) > 0) { + nextEra = eminusone; + } else if (eplusone != null && eplusone.compareTo(eraData) > 0) { + nextEra = eplusone; + } if (startCal == null && prevEra != null && prevEra.getEnd() != null) { - startCal = forDateString(prevEra.getEnd()); + startCal = prevEra.getEndCalendar(); // shift forward so we are in the next era startCal.setTimeInMillis(startCal.getTimeInMillis() + (DateConstants.MILLIS_PER_DAY)); } if (endCal == null && nextEra != null && nextEra.getStart() != null) { - endCal = forDateString(nextEra.getStart()); + endCal = nextEra.getStartCalendar(); // shift backward so we are in the prev era endCal.setTimeInMillis(endCal.getTimeInMillis() - (DateConstants.MILLIS_PER_DAY)); } @@ -3150,7 +3165,7 @@ private void handleEras(XPathParts parts, String value, List examples) { sampleDate.setTimeInMillis( sampleDate.getTimeInMillis() - (DateConstants.MILLIS_PER_DAY)); } else if (startCal != null && endCal == null) { - sampleDate = forDateString("2002-07-15"); // CLDR repo root commit + sampleDate = new GregorianCalendar(2002, 6, 15); // CLDR repo root commit if (sampleDate.before(startCal)) { sampleDate = startCal; sampleDate.setTimeInMillis( @@ -3183,24 +3198,6 @@ private void handleEras(XPathParts parts, String value, List examples) { examples.add(sdf.format(sample)); } - private GregorianCalendar forDateString(String ymd) { - if (ymd == null) return null; - int multiplier = 1; - if (ymd.startsWith("-")) { - multiplier = -1; - ymd = ymd.substring(1); - } - final String[] parts = ymd.split("-"); - try { - return new GregorianCalendar( - multiplier * Integer.parseInt(parts[0]), - Integer.parseInt(parts[1]) - 1, - Integer.parseInt(parts[2])); - } catch (NumberFormatException nfe) { - throw new IllegalArgumentException("While parsing date string " + ymd, nfe); - } - } - /** * Add examples for quarters for the gregorian calendar, matching each quarter type (1, 2, 3, 4) * to a corresponding sample month and formatting an example with that date diff --git a/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalCalendarData.java b/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalCalendarData.java index 4cbf70f93b4..3a4af107233 100644 --- a/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalCalendarData.java +++ b/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalCalendarData.java @@ -1,6 +1,7 @@ package org.unicode.cldr.util; import com.ibm.icu.impl.locale.XCldrStub.ImmutableMap; +import com.ibm.icu.util.GregorianCalendar; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -10,7 +11,7 @@ public class SupplementalCalendarData implements Iterable { /** an element */ - public static class EraData { + public static class EraData implements Comparable { static final int INDEX = 4; // index of our element /** the xpath */ @@ -33,6 +34,14 @@ public String getEnd() { return xpath.getAttributeValue(INDEX, LDMLConstants.END); } + public GregorianCalendar getStartCalendar() { + return forDateString(getStart()); + } + + public GregorianCalendar getEndCalendar() { + return forDateString(getEnd()); + } + public String getCode() { return xpath.getAttributeValue(INDEX, LDMLConstants.CODE); } @@ -49,6 +58,44 @@ public String toString() { "ERA %d, [%s-%s] code=%s, aliases=%s", getType(), getStart(), getEnd(), getCode(), String.join(",", getAliases())); } + + private static final GregorianCalendar forDateString(String ymd) { + if (ymd == null) return null; + int multiplier = 1; + if (ymd.startsWith("-")) { + multiplier = -1; + ymd = ymd.substring(1); + } + final String[] parts = ymd.split("-"); + try { + return new GregorianCalendar( + multiplier * Integer.parseInt(parts[0]), + Integer.parseInt(parts[1]) - 1, + Integer.parseInt(parts[2])); + } catch (NumberFormatException nfe) { + throw new IllegalArgumentException("While parsing date string " + ymd, nfe); + } + } + + private GregorianCalendar getLatest() { + GregorianCalendar l = getEndCalendar(); + if (l == null) l = getStartCalendar(); + return l; + } + + /** only works within the same cal system */ + @Override + public int compareTo(EraData o) { + final GregorianCalendar l = getLatest(); + final GregorianCalendar ol = o.getLatest(); + if (l == null || ol == null) { + // compare by id + return Integer.compare(getType(), o.getType()); + } else { + // compare by date + return l.compareTo(ol); + } + } } /** a element */ From 9f1c860db394fc77004e5c9b6e4f550255144263 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Mon, 28 Apr 2025 17:50:38 -0500 Subject: [PATCH 6/8] CLDR-17851 update to supplemental calendar date parser per discuss - memoize to only parse once, make items freezable - store values in fields rather than storing XPathValue --- .../cldr/util/SupplementalCalendarData.java | 102 ++++++++++++------ 1 file changed, 71 insertions(+), 31 deletions(-) diff --git a/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalCalendarData.java b/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalCalendarData.java index 3a4af107233..9903d8bf49d 100644 --- a/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalCalendarData.java +++ b/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalCalendarData.java @@ -1,6 +1,9 @@ package org.unicode.cldr.util; -import com.ibm.icu.impl.locale.XCldrStub.ImmutableMap; +import com.google.common.base.Suppliers; +import com.google.common.collect.ComparisonChain; +import com.google.common.collect.ImmutableMap; +import com.ibm.icu.util.Freezable; import com.ibm.icu.util.GregorianCalendar; import java.util.HashMap; import java.util.Iterator; @@ -15,35 +18,48 @@ public static class EraData implements Comparable { static final int INDEX = 4; // index of our element /** the xpath */ - private final XPathValue xpath; + private final String start; + + private final String end; + private final String code; + private final int type; + private final String calendarType; // for comparison + private final GregorianCalendar startCalendar; + private final GregorianCalendar endCalendar; EraData(final XPathValue xpath) { - this.xpath = xpath; - // TODO: validate (in unit test) + calendarType = xpath.getAttributeValue(2, LDMLConstants.TYPE); + type = Integer.parseInt(xpath.getAttributeValue(INDEX, LDMLConstants.TYPE)); + + start = xpath.getAttributeValue(INDEX, LDMLConstants.START); + startCalendar = forDateString(start); + end = xpath.getAttributeValue(INDEX, LDMLConstants.END); + endCalendar = forDateString(end); + code = xpath.getAttributeValue(INDEX, LDMLConstants.CODE); } public int getType() { - return Integer.parseInt(xpath.getAttributeValue(INDEX, LDMLConstants.TYPE)); + return type; } public String getStart() { - return xpath.getAttributeValue(INDEX, LDMLConstants.START); + return start; } public String getEnd() { - return xpath.getAttributeValue(INDEX, LDMLConstants.END); + return end; } public GregorianCalendar getStartCalendar() { - return forDateString(getStart()); + return startCalendar; } public GregorianCalendar getEndCalendar() { - return forDateString(getEnd()); + return endCalendar; } public String getCode() { - return xpath.getAttributeValue(INDEX, LDMLConstants.CODE); + return code; } public String[] getAliases() { @@ -86,38 +102,32 @@ private GregorianCalendar getLatest() { /** only works within the same cal system */ @Override public int compareTo(EraData o) { - final GregorianCalendar l = getLatest(); - final GregorianCalendar ol = o.getLatest(); - if (l == null || ol == null) { - // compare by id - return Integer.compare(getType(), o.getType()); - } else { - // compare by date - return l.compareTo(ol); - } + return ComparisonChain.start() + .compare(calendarType, o.calendarType) + .compare(getLatest(), o.getLatest()) + .compare(getType(), o.getType()) + .result(); } } /** a element */ - public static class CalendarData implements Iterable { + public static class CalendarData implements Iterable, Freezable { static final int INDEX = 3; // index of our element /** the xpath */ - private XPathValue system = null; + private String system = null; /** the xpath */ - private XPathValue inheritEras = null; + private String inheritEras = null; private Map eras = new HashMap(); public String getSystemType() { - if (system == null) return null; - return system.getAttributeValue(INDEX, LDMLConstants.TYPE); + return system; } public String getInheritEras() { - if (inheritEras == null) return null; - return inheritEras.getAttributeValue(INDEX, LDMLConstants.CALENDAR); + return inheritEras; } @Override @@ -128,6 +138,32 @@ public Iterator iterator() { public EraData get(Integer era) { return eras.get(era); } + + @Override + public CalendarData cloneAsThawed() { + throw new UnsupportedOperationException("Unimplemented 'cloneAsThawed'"); + } + + @Override + public CalendarData freeze() { + eras = ImmutableMap.copyOf(eras); + return this; + } + + @Override + public boolean isFrozen() { + return (eras instanceof ImmutableMap); + } + + void setSystemXPath(XPathValue xpath) { + if (isFrozen()) throw new UnsupportedOperationException("frozen"); + system = xpath.getAttributeValue(INDEX, LDMLConstants.TYPE); + } + + void setInheritEras(XPathValue xpath) { + if (isFrozen()) throw new UnsupportedOperationException("frozen"); + inheritEras = xpath.getAttributeValue(INDEX, LDMLConstants.CALENDAR); + } } private Map typeToCalendar = new HashMap<>(); @@ -194,25 +230,29 @@ private void acceptInheritEras(XPathValue x, CalendarData c) { if (c.inheritEras != null) { throw new IllegalArgumentException("Duplicate calendar inheritEras: " + x); } - c.inheritEras = x; + c.setInheritEras(x); } private void acceptCalendarSystem(XPathValue x, CalendarData c) { if (c.system != null) { throw new IllegalArgumentException("Duplicate calendar system: " + x); } - c.system = x; + c.setSystemXPath(x); } + final Supplier supplier = + Suppliers.memoize(() -> new SupplementalCalendarData(typeToCalendar)); + + /** Calling get() freezes the data, so, only call get() once all data is loaded. */ @Override public SupplementalCalendarData get() { - return new SupplementalCalendarData(typeToCalendar); + return supplier.get(); } } private SupplementalCalendarData(Map m) { - // TODO: freeze all types - // m.forEach(c -> c.freeze()); + // freeze all types + m.values().forEach(c -> c.freeze()); this.typeToCalendar = ImmutableMap.copyOf(m); } From 3339539aa1246e3cb9a0a326b73f9b4486b37c69 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Mon, 28 Apr 2025 17:53:33 -0500 Subject: [PATCH 7/8] CLDR-17851 update per review comments - roll by 2 days instead of one --- .../java/org/unicode/cldr/test/ExampleGenerator.java | 11 ++++++----- .../java/org/unicode/cldr/util/DateConstants.java | 1 + 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tools/cldr-code/src/main/java/org/unicode/cldr/test/ExampleGenerator.java b/tools/cldr-code/src/main/java/org/unicode/cldr/test/ExampleGenerator.java index ebef35be7cc..66517cc1f96 100644 --- a/tools/cldr-code/src/main/java/org/unicode/cldr/test/ExampleGenerator.java +++ b/tools/cldr-code/src/main/java/org/unicode/cldr/test/ExampleGenerator.java @@ -3144,12 +3144,13 @@ private void handleEras(XPathParts parts, String value, List examples) { if (startCal == null && prevEra != null && prevEra.getEnd() != null) { startCal = prevEra.getEndCalendar(); // shift forward so we are in the next era - startCal.setTimeInMillis(startCal.getTimeInMillis() + (DateConstants.MILLIS_PER_DAY)); + startCal.setTimeInMillis( + startCal.getTimeInMillis() + DateConstants.MILLIS_PER_TWO_DAYS); } if (endCal == null && nextEra != null && nextEra.getStart() != null) { endCal = nextEra.getStartCalendar(); // shift backward so we are in the prev era - endCal.setTimeInMillis(endCal.getTimeInMillis() - (DateConstants.MILLIS_PER_DAY)); + endCal.setTimeInMillis(endCal.getTimeInMillis() - DateConstants.MILLIS_PER_TWO_DAYS); } GregorianCalendar sampleDate = null; @@ -3158,18 +3159,18 @@ private void handleEras(XPathParts parts, String value, List examples) { // roll back a day to not hit the edge sampleDate = endCal; sampleDate.setTimeInMillis( - sampleDate.getTimeInMillis() - (DateConstants.MILLIS_PER_DAY)); + sampleDate.getTimeInMillis() - DateConstants.MILLIS_PER_TWO_DAYS); } else if (startCal == null && endCal != null) { // roll back a day to not hit the edge sampleDate = endCal; sampleDate.setTimeInMillis( - sampleDate.getTimeInMillis() - (DateConstants.MILLIS_PER_DAY)); + sampleDate.getTimeInMillis() - DateConstants.MILLIS_PER_TWO_DAYS); } else if (startCal != null && endCal == null) { sampleDate = new GregorianCalendar(2002, 6, 15); // CLDR repo root commit if (sampleDate.before(startCal)) { sampleDate = startCal; sampleDate.setTimeInMillis( - sampleDate.getTimeInMillis() + (DateConstants.MILLIS_PER_DAY)); + sampleDate.getTimeInMillis() + DateConstants.MILLIS_PER_TWO_DAYS); } } else { // System.err.println("No good date for " + eraData); diff --git a/tools/cldr-code/src/main/java/org/unicode/cldr/util/DateConstants.java b/tools/cldr-code/src/main/java/org/unicode/cldr/util/DateConstants.java index 9842e006d9a..4a35f663e07 100644 --- a/tools/cldr-code/src/main/java/org/unicode/cldr/util/DateConstants.java +++ b/tools/cldr-code/src/main/java/org/unicode/cldr/util/DateConstants.java @@ -15,6 +15,7 @@ public class DateConstants { public static final long MILLIS_PER_MINUTE = MILLIS_PER_SECOND * 60; public static final long MILLIS_PER_HOUR = MILLIS_PER_MINUTE * 60; public static final long MILLIS_PER_DAY = MILLIS_PER_HOUR * 24; + public static final long MILLIS_PER_TWO_DAYS = 2 * MILLIS_PER_DAY; public static final long MILLIS_PER_MONTH = MILLIS_PER_DAY * 30; private static final Date getRecentHistory(Date d) { From c45a6eca9b3ba2f65dbc979fe192630ffb82facd Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Tue, 29 Apr 2025 15:22:49 -0500 Subject: [PATCH 8/8] wip --- .../cldr/util/SupplementalCalendarData.java | 57 +++++++++++++------ 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalCalendarData.java b/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalCalendarData.java index 9903d8bf49d..a1ef167afc7 100644 --- a/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalCalendarData.java +++ b/tools/cldr-code/src/main/java/org/unicode/cldr/util/SupplementalCalendarData.java @@ -1,12 +1,16 @@ package org.unicode.cldr.util; +import com.google.common.base.CharMatcher; +import com.google.common.base.Splitter; import com.google.common.base.Suppliers; import com.google.common.collect.ComparisonChain; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.ibm.icu.util.Freezable; import com.ibm.icu.util.GregorianCalendar; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.function.Consumer; import java.util.function.Supplier; @@ -24,20 +28,44 @@ public static class EraData implements Comparable { private final String code; private final int type; private final String calendarType; // for comparison - private final GregorianCalendar startCalendar; - private final GregorianCalendar endCalendar; + private final long startTime; + private final long endTime; + + private final List aliases; EraData(final XPathValue xpath) { calendarType = xpath.getAttributeValue(2, LDMLConstants.TYPE); type = Integer.parseInt(xpath.getAttributeValue(INDEX, LDMLConstants.TYPE)); start = xpath.getAttributeValue(INDEX, LDMLConstants.START); - startCalendar = forDateString(start); + final GregorianCalendar startCalendar = dateStringToCalendar(start); + if (startCalendar != null) { + startTime = startCalendar.getTimeInMillis(); + } else { + startTime = Long.MIN_VALUE; + } + end = xpath.getAttributeValue(INDEX, LDMLConstants.END); - endCalendar = forDateString(end); + final GregorianCalendar endCalendar = dateStringToCalendar(end); + if (endCalendar != null) { + endTime = endCalendar.getTimeInMillis(); + } else { + endTime = Long.MAX_VALUE; + } + code = xpath.getAttributeValue(INDEX, LDMLConstants.CODE); + + final String aliasString = xpath.getAttributeValue(INDEX, LDMLConstants.ALIASES); + if (aliasString == null) { + aliases = ImmutableList.of(); + } else { + aliases = LIST_SPLITTER.splitToList(aliasString); + } } + private static final Splitter LIST_SPLITTER = + Splitter.on(CharMatcher.whitespace()).omitEmptyStrings(); + public int getType() { return type; } @@ -50,32 +78,30 @@ public String getEnd() { return end; } - public GregorianCalendar getStartCalendar() { - return startCalendar; + public long getStartTime() { + return startTime; } - public GregorianCalendar getEndCalendar() { - return endCalendar; + public long getEndTime() { + return endTime; } public String getCode() { return code; } - public String[] getAliases() { - final String s = xpath.getAttributeValue(INDEX, LDMLConstants.ALIASES); - if (s == null) return "".split(" "); // empty array - return s.split(" "); + public List getAliases() { + return aliases; } @Override public String toString() { return String.format( "ERA %d, [%s-%s] code=%s, aliases=%s", - getType(), getStart(), getEnd(), getCode(), String.join(",", getAliases())); + getType(), getStart(), getEnd(), getCode(), getAliases()); } - private static final GregorianCalendar forDateString(String ymd) { + private static final GregorianCalendar dateStringToCalendar(String ymd) { if (ymd == null) return null; int multiplier = 1; if (ymd.startsWith("-")) { @@ -104,8 +130,7 @@ private GregorianCalendar getLatest() { public int compareTo(EraData o) { return ComparisonChain.start() .compare(calendarType, o.calendarType) - .compare(getLatest(), o.getLatest()) - .compare(getType(), o.getType()) + .compare(getLatestTime(), o.getLatestTime()) .result(); } }