in make/jdk/src/classes/build/tools/cldrconverter/LDMLParseHandler.java [73:874]
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
String type;
String prefix;
switch (qName) {
//
// Generic information
//
case "identity":
// ignore this element - it has language and territory elements that aren't locale data
pushIgnoredContainer(qName);
break;
// for LocaleNames
// copy string
case "localeSeparator":
pushStringEntry(qName, attributes,
CLDRConverter.LOCALE_SEPARATOR);
break;
case "localeKeyTypePattern":
pushStringEntry(qName, attributes,
CLDRConverter.LOCALE_KEYTYPE);
break;
case "language":
case "script":
case "territory":
case "variant":
// for LocaleNames
// copy string
pushStringEntry(qName, attributes,
CLDRConverter.LOCALE_NAME_PREFIX +
(qName.equals("variant") ? "%%" : "") +
attributes.getValue("type"));
break;
case "key":
// for LocaleNames
// copy string
{
String key = convertOldKeyName(attributes.getValue("type"));
if (key.length() == 2) {
pushStringEntry(qName, attributes,
CLDRConverter.LOCALE_KEY_PREFIX + key);
} else {
pushIgnoredContainer(qName);
}
}
break;
case "type":
// for LocaleNames/CalendarNames
// copy string
// ignore scope="core"
{
String key = convertOldKeyName(attributes.getValue("key"));
String scope = attributes.getValue("scope");
if (key.length() == 2 && scope == null) {
pushStringEntry(qName, attributes,
CLDRConverter.LOCALE_TYPE_PREFIX + key + "." +
attributes.getValue("type"));
} else {
pushIgnoredContainer(qName);
}
}
break;
//
// Currency information
//
case "currency":
// for CurrencyNames
// stash away "type" value for nested <symbol>
pushKeyContainer(qName, attributes, attributes.getValue("type"));
break;
case "symbol":
// for CurrencyNames
// need to get the key from the containing <currency> element
pushStringEntry(qName, attributes, CLDRConverter.CURRENCY_SYMBOL_PREFIX
+ getContainerKey());
break;
// Calendar or currency
case "displayName":
{
if (currentContainer.getqName().equals("field")) {
pushStringEntry(qName, attributes,
(currentCalendarType != null ? currentCalendarType.keyElementName() : "")
+ "field." + getContainerKey());
} else {
// for CurrencyNames
// need to get the key from the containing <currency> element
// ignore if is has "count" attribute
String containerKey = getContainerKey();
if (containerKey != null && attributes.getValue("count") == null) {
pushStringEntry(qName, attributes,
CLDRConverter.CURRENCY_NAME_PREFIX
+ containerKey.toLowerCase(Locale.ROOT),
attributes.getValue("type"));
} else {
pushIgnoredContainer(qName);
}
}
}
break;
//
// Calendar information
//
case "calendar":
{
// mostly for FormatData (CalendarData items firstDay and minDays are also nested)
// use only if it's supported by java.util.Calendar.
String calendarName = attributes.getValue("type");
currentCalendarType = CalendarType.forName(calendarName);
if (currentCalendarType != null) {
pushContainer(qName, attributes);
} else {
pushIgnoredContainer(qName);
}
}
break;
case "fields":
{
pushContainer(qName, attributes);
}
break;
case "field":
{
type = attributes.getValue("type");
switch (type) {
case "era":
case "year":
case "month":
case "week":
case "weekday":
case "dayperiod":
case "hour":
case "minute":
case "second":
case "zone":
pushKeyContainer(qName, attributes, type);
break;
default:
pushIgnoredContainer(qName);
break;
}
}
break;
case "monthContext":
{
// for FormatData
// need to keep stand-alone and format, to allow for inheritance in CLDR
type = attributes.getValue("type");
if ("stand-alone".equals(type) || "format".equals(type)) {
currentContext = type;
pushKeyContainer(qName, attributes, type);
} else {
pushIgnoredContainer(qName);
}
}
break;
case "monthWidth":
{
// for FormatData
// create string array for the two types that the JRE knows
// keep info about the context type so we can sort out inheritance later
if (currentCalendarType == null) {
pushIgnoredContainer(qName);
break;
}
prefix = (currentCalendarType == null) ? "" : currentCalendarType.keyElementName();
currentWidth = attributes.getValue("type");
switch (currentWidth) {
case "wide":
pushStringArrayEntry(qName, attributes, prefix + "MonthNames/" + getContainerKey(), 13);
break;
case "abbreviated":
pushStringArrayEntry(qName, attributes, prefix + "MonthAbbreviations/" + getContainerKey(), 13);
break;
case "narrow":
pushStringArrayEntry(qName, attributes, prefix + "MonthNarrows/" + getContainerKey(), 13);
break;
default:
pushIgnoredContainer(qName);
break;
}
}
break;
case "month":
// for FormatData
// add to string array entry of monthWidth element
pushStringArrayElement(qName, attributes, Integer.parseInt(attributes.getValue("type")) - 1);
break;
case "dayContext":
{
// for FormatData
// need to keep stand-alone and format, to allow for multiple inheritance in CLDR
type = attributes.getValue("type");
if ("stand-alone".equals(type) || "format".equals(type)) {
currentContext = type;
pushKeyContainer(qName, attributes, type);
} else {
pushIgnoredContainer(qName);
}
}
break;
case "dayWidth":
{
// for FormatData
// create string array for the two types that the JRE knows
// keep info about the context type so we can sort out inheritance later
prefix = (currentCalendarType == null) ? "" : currentCalendarType.keyElementName();
currentWidth = attributes.getValue("type");
switch (currentWidth) {
case "wide":
pushStringArrayEntry(qName, attributes, prefix + "DayNames/" + getContainerKey(), 7);
break;
case "abbreviated":
pushStringArrayEntry(qName, attributes, prefix + "DayAbbreviations/" + getContainerKey(), 7);
break;
case "narrow":
pushStringArrayEntry(qName, attributes, prefix + "DayNarrows/" + getContainerKey(), 7);
break;
default:
pushIgnoredContainer(qName);
break;
}
}
break;
case "day":
// for FormatData
// add to string array entry of monthWidth element
pushStringArrayElement(qName, attributes, Integer.parseInt(DAY_OF_WEEK_MAP.get(attributes.getValue("type"))) - 1);
break;
case "dayPeriodContext":
// for FormatData
// need to keep stand-alone and format, to allow for multiple inheritance in CLDR
{
type = attributes.getValue("type");
if ("stand-alone".equals(type) || "format".equals(type)) {
currentContext = type;
pushKeyContainer(qName, attributes, type);
} else {
pushIgnoredContainer(qName);
}
}
break;
case "dayPeriodWidth":
// for FormatData
// create string array entry for am/pm.
currentWidth = attributes.getValue("type");
switch (currentWidth) {
case "wide":
pushStringArrayEntry(qName, attributes, "AmPmMarkers/" + getContainerKey(), 12);
break;
case "narrow":
pushStringArrayEntry(qName, attributes, "narrow.AmPmMarkers/" + getContainerKey(), 12);
break;
case "abbreviated":
pushStringArrayEntry(qName, attributes, "abbreviated.AmPmMarkers/" + getContainerKey(), 12);
break;
default:
pushIgnoredContainer(qName);
break;
}
break;
case "dayPeriod":
// for FormatData
// add to string array entry of AmPmMarkers element
if (attributes.getValue("alt") == null) {
switch (attributes.getValue("type")) {
case "am":
pushStringArrayElement(qName, attributes, 0);
break;
case "pm":
pushStringArrayElement(qName, attributes, 1);
break;
case "midnight":
pushStringArrayElement(qName, attributes, 2);
break;
case "noon":
pushStringArrayElement(qName, attributes, 3);
break;
case "morning1":
pushStringArrayElement(qName, attributes, 4);
break;
case "morning2":
pushStringArrayElement(qName, attributes, 5);
break;
case "afternoon1":
pushStringArrayElement(qName, attributes, 6);
break;
case "afternoon2":
pushStringArrayElement(qName, attributes, 7);
break;
case "evening1":
pushStringArrayElement(qName, attributes, 8);
break;
case "evening2":
pushStringArrayElement(qName, attributes, 9);
break;
case "night1":
pushStringArrayElement(qName, attributes, 10);
break;
case "night2":
pushStringArrayElement(qName, attributes, 11);
break;
default:
pushIgnoredContainer(qName);
break;
}
} else {
// discard alt values
pushIgnoredContainer(qName);
}
break;
case "eraNames":
// CLDR era names are inconsistent in terms of their lengths. For example,
// the full names of Japanese imperial eras are eraAbbr, while the full names
// of the Julian eras are eraNames.
if (currentCalendarType == null) {
assert currentContainer instanceof IgnoredContainer;
pushIgnoredContainer(qName);
} else {
String key = currentCalendarType.keyElementName() + "long.Eras"; // for now
pushStringArrayEntry(qName, attributes, key, currentCalendarType.getEraLength(qName));
}
break;
case "eraAbbr":
// for FormatData
// create string array entry
if (currentCalendarType == null) {
assert currentContainer instanceof IgnoredContainer;
pushIgnoredContainer(qName);
} else {
String key = currentCalendarType.keyElementName() + "Eras";
pushStringArrayEntry(qName, attributes, key, currentCalendarType.getEraLength(qName));
}
break;
case "eraNarrow":
// mainly used for the Japanese imperial calendar
if (currentCalendarType == null) {
assert currentContainer instanceof IgnoredContainer;
pushIgnoredContainer(qName);
} else {
String key = currentCalendarType.keyElementName() + "narrow.Eras";
pushStringArrayEntry(qName, attributes, key, currentCalendarType.getEraLength(qName));
}
break;
case "era":
// for FormatData
// add to string array entry of eraAbbr element
if (currentCalendarType == null) {
assert currentContainer instanceof IgnoredContainer;
pushIgnoredContainer(qName);
} else {
int index = Integer.parseInt(attributes.getValue("type"));
index = currentCalendarType.normalizeEraIndex(index);
if (index >= 0) {
pushStringArrayElement(qName, attributes, index);
} else {
pushIgnoredContainer(qName);
}
if (currentContainer.getParent() == null) {
throw new InternalError("currentContainer: null parent");
}
}
break;
case "quarterContext":
{
// for FormatData
// need to keep stand-alone and format, to allow for inheritance in CLDR
type = attributes.getValue("type");
if ("stand-alone".equals(type) || "format".equals(type)) {
currentContext = type;
pushKeyContainer(qName, attributes, type);
} else {
pushIgnoredContainer(qName);
}
}
break;
case "quarterWidth":
{
// for FormatData
// keep info about the context type so we can sort out inheritance later
prefix = (currentCalendarType == null) ? "" : currentCalendarType.keyElementName();
currentWidth = attributes.getValue("type");
switch (currentWidth) {
case "wide":
pushStringArrayEntry(qName, attributes, prefix + "QuarterNames/" + getContainerKey(), 4);
break;
case "abbreviated":
pushStringArrayEntry(qName, attributes, prefix + "QuarterAbbreviations/" + getContainerKey(), 4);
break;
case "narrow":
pushStringArrayEntry(qName, attributes, prefix + "QuarterNarrows/" + getContainerKey(), 4);
break;
default:
pushIgnoredContainer(qName);
break;
}
}
break;
case "quarter":
// for FormatData
// add to string array entry of quarterWidth element
pushStringArrayElement(qName, attributes, Integer.parseInt(attributes.getValue("type")) - 1);
break;
//
// Time zone names
//
case "timeZoneNames":
pushContainer(qName, attributes);
break;
case "hourFormat":
pushStringEntry(qName, attributes, "timezone.hourFormat");
break;
case "gmtFormat":
pushStringEntry(qName, attributes, "timezone.gmtFormat");
break;
case "gmtZeroFormat":
pushStringEntry(qName, attributes, "timezone.gmtZeroFormat");
break;
case "regionFormat":
{
type = attributes.getValue("type");
pushStringEntry(qName, attributes, "timezone.regionFormat" +
(type == null ? "" : "." + type));
}
break;
case "zone":
{
String tzid = attributes.getValue("type"); // Olson tz id
zonePrefix = CLDRConverter.TIMEZONE_ID_PREFIX;
put(zonePrefix + tzid, new HashMap<String, String>());
pushKeyContainer(qName, attributes, tzid);
}
break;
case "metazone":
{
String zone = attributes.getValue("type"); // LDML meta zone id
zonePrefix = CLDRConverter.METAZONE_ID_PREFIX;
put(zonePrefix + zone, new HashMap<String, String>());
pushKeyContainer(qName, attributes, zone);
}
break;
case "long":
zoneNameStyle = "long";
pushContainer(qName, attributes);
break;
case "short":
zoneNameStyle = "short";
pushContainer(qName, attributes);
break;
case "generic": // generic name
case "standard": // standard time name
case "daylight": // daylight saving (summer) time name
pushStringEntry(qName, attributes, CLDRConverter.ZONE_NAME_PREFIX + qName + "." + zoneNameStyle);
break;
case "exemplarCity":
pushStringEntry(qName, attributes, CLDRConverter.EXEMPLAR_CITY_PREFIX);
break;
//
// Number format information
//
case "decimalFormatLength":
type = attributes.getValue("type");
if (null == type) {
// format data for decimal number format
pushStringEntry(qName, attributes,
currentNumberingSystem + "NumberPatterns/decimal");
currentStyle = type;
} else {
switch (type) {
case "short":
case "long":
// considering "short" and long for
// compact number formatting patterns
pushKeyContainer(qName, attributes, type);
currentStyle = type;
break;
default:
pushIgnoredContainer(qName);
break;
}
}
break;
case "decimalFormat":
if(currentStyle == null) {
pushContainer(qName, attributes);
} else {
switch (currentStyle) {
case "short":
case "long":
pushStringListEntry(qName, attributes,
currentStyle+".CompactNumberPatterns");
break;
default:
pushIgnoredContainer(qName);
break;
}
}
break;
case "currencyFormat":
case "percentFormat":
pushKeyContainer(qName, attributes, attributes.getValue("type"));
break;
case "pattern":
String containerName = currentContainer.getqName();
switch (containerName) {
case "currencyFormat":
case "percentFormat":
{
// for FormatData
// copy string for later assembly into NumberPatterns
if (currentContainer instanceof KeyContainer) {
if (attributes.getValue("alt") != null) {
// ignore "alphaNextToNumber", "noCurrency" for currency for now.
pushIgnoredContainer(qName);
} else {
String fStyle = ((KeyContainer)currentContainer).getKey();
if (fStyle.equals("standard")) {
pushStringEntry(qName, attributes,
currentNumberingSystem + "NumberPatterns/" + containerName.replaceFirst("Format", ""));
} else if (fStyle.equals("accounting") && containerName.equals("currencyFormat")) {
pushStringEntry(qName, attributes,
currentNumberingSystem + "NumberPatterns/accounting");
} else {
pushIgnoredContainer(qName);
}
}
} else {
pushIgnoredContainer(qName);
}
}
break;
case "decimalFormat":
if (currentStyle == null) {
pushContainer(qName, attributes);
} else {
switch (currentStyle) {
case "short":
case "long":
pushStringListElement(qName, attributes,
(int) Math.log10(Double.parseDouble(attributes.getValue("type"))),
attributes.getValue("count"));
break;
default:
pushIgnoredContainer(qName);
break;
}
}
break;
case "dateFormat":
case "timeFormat":
case "dateTimeFormat":
// for FormatData
// copy string for later assembly into DateTimePatterns
if (currentContainer instanceof KeyContainer kc && !"standard".equals(kc.getKey())) {
// only take the standard value for now, ignoring specialized one,
// such as "atTime" for "dateTimeFormat"
pushIgnoredContainer(qName);
} else {
prefix = (currentCalendarType == null) ? "" : currentCalendarType.keyElementName();
pushStringEntry(qName, attributes, prefix + "DateTimePatterns/" + currentStyle +
switch (containerName) {
case "dateFormat" -> "-date";
case "timeFormat" -> "-time";
case "dateTimeFormat" -> "-dateTime";
default -> throw new InternalError();
});
}
break;
default:
pushContainer(qName, attributes);
break;
}
break;
case "currencyFormats":
case "decimalFormats":
case "percentFormats":
{
String script = attributes.getValue("numberSystem");
if (script != null) {
addNumberingScript(script);
currentNumberingSystem = script + ".";
}
pushContainer(qName, attributes);
}
break;
case "currencyFormatLength":
if (attributes.getValue("type") == null) {
// skipping type="short" data
// for FormatData
pushContainer(qName, attributes);
} else {
pushIgnoredContainer(qName);
}
break;
case "defaultNumberingSystem":
// default numbering system if multiple numbering systems are used.
pushStringEntry(qName, attributes, "DefaultNumberingSystem");
break;
case "symbols":
// for FormatData
// look up numberingSystems
symbols: {
String script = attributes.getValue("numberSystem");
if (script == null) {
// Has no script. Just ignore.
pushIgnoredContainer(qName);
break;
}
// Use keys as <script>."NumberElements/<symbol>"
currentNumberingSystem = script + ".";
String digits = CLDRConverter.handlerNumbering.get(script);
if (digits == null) {
pushIgnoredContainer(qName);
break;
}
addNumberingScript(script);
put(currentNumberingSystem + "NumberElements/zero", digits.substring(0, 1));
pushContainer(qName, attributes);
}
break;
case "decimal":
case "group":
case "currencyDecimal":
case "currencyGroup":
// for FormatData
// copy string for later assembly into NumberElements
if (currentContainer.getqName().equals("symbols")) {
pushStringEntry(qName, attributes, currentNumberingSystem + "NumberElements/" + qName);
} else {
pushIgnoredContainer(qName);
}
break;
case "list":
// for FormatData
// copy string for later assembly into NumberElements
pushStringEntry(qName, attributes, currentNumberingSystem + "NumberElements/list");
break;
case "percentSign":
// for FormatData
// copy string for later assembly into NumberElements
pushStringEntry(qName, attributes, currentNumberingSystem + "NumberElements/percent");
break;
case "nativeZeroDigit":
// for FormatData
// copy string for later assembly into NumberElements
pushStringEntry(qName, attributes, currentNumberingSystem + "NumberElements/zero");
break;
case "patternDigit":
// for FormatData
// copy string for later assembly into NumberElements
pushStringEntry(qName, attributes, currentNumberingSystem + "NumberElements/pattern");
break;
case "plusSign":
// TODO: DecimalFormatSymbols doesn't support plusSign
pushIgnoredContainer(qName);
break;
case "minusSign":
// for FormatData
// copy string for later assembly into NumberElements
pushStringEntry(qName, attributes, currentNumberingSystem + "NumberElements/minus");
break;
case "exponential":
// for FormatData
// copy string for later assembly into NumberElements
pushStringEntry(qName, attributes, currentNumberingSystem + "NumberElements/exponential");
break;
case "perMille":
// for FormatData
// copy string for later assembly into NumberElements
pushStringEntry(qName, attributes, currentNumberingSystem + "NumberElements/permille");
break;
case "infinity":
// for FormatData
// copy string for later assembly into NumberElements
pushStringEntry(qName, attributes, currentNumberingSystem + "NumberElements/infinity");
break;
case "nan":
// for FormatData
// copy string for later assembly into NumberElements
pushStringEntry(qName, attributes, currentNumberingSystem + "NumberElements/nan");
break;
case "dateFormatLength":
case "timeFormatLength":
case "dateTimeFormatLength":
currentStyle = attributes.getValue("type");
pushContainer(qName, attributes);
break;
case "dateFormats":
case "timeFormats":
case "dateTimeFormats":
pushContainer(qName, attributes);
break;
case "dateFormat":
case "timeFormat":
case "dateTimeFormat":
pushKeyContainer(qName, attributes, attributes.getValue("type"));
break;
case "dateFormatItem":
{
// for FormatData
if (currentCalendarType != null) {
var skeleton = attributes.getValue("id");
CLDRConverter.availableSkeletons.add(skeleton);
pushStringEntry(qName, attributes,
currentCalendarType.keyElementName() + Bundle.DATEFORMATITEM_KEY_PREFIX + skeleton);
} else {
pushIgnoredContainer(qName);
}
}
break;
case "localizedPatternChars":
{
// for FormatData
// copy string for later adaptation to JRE use
prefix = (currentCalendarType == null) ? "" : currentCalendarType.keyElementName();
pushStringEntry(qName, attributes, prefix + "DateTimePatternChars");
}
break;
// "alias" for root
case "alias":
{
if (id.equals("root") && !isIgnored(attributes)
&& ((currentContainer.getqName().equals("decimalFormatLength"))
|| (currentContainer.getqName().equals("currencyFormat"))
|| (currentContainer.getqName().equals("percentFormat"))
|| (currentContainer.getqName().equals("listPattern"))
|| (currentCalendarType != null && !currentCalendarType.lname().startsWith("islamic-")))) { // ignore islamic variants
pushAliasEntry(qName, attributes, attributes.getValue("path"));
} else {
pushIgnoredContainer(qName);
}
}
break;
// ListPatterns
case "listPattern":
currentStyle = Optional.ofNullable(attributes.getValue("type")).orElse("standard");
pushStringArrayEntry(qName, attributes, "ListPatterns_" + currentStyle, 5);
break;
case "listPatternPart":
type = attributes.getValue("type");
pushStringArrayElement(qName, attributes,
switch (type) {
case "start" -> 0;
case "middle" -> 1;
case "end" -> 2;
case "2" -> 3;
case "3" -> 4;
default -> throw new IllegalArgumentException(
"""
The "type" attribute value for "listPatternPart" element is not recognized: %s
""".formatted(type)
);
});
break;
// Lenient parsing
case "parseLenients":
if ("lenient".equals(attributes.getValue("level"))) {
pushKeyContainer(qName, attributes, attributes.getValue("scope"));
} else {
pushIgnoredContainer(qName);
}
break;
case "parseLenient":
// Use only the lenient minus sign for now
if (currentContainer instanceof KeyContainer kc
&& kc.getKey().equals("number")
&& attributes.getValue("sample").equals("-")) {
pushStringEntry(qName, attributes, currentNumberingSystem + "NumberElements/lenientMinusSigns");
} else {
pushIgnoredContainer(qName);
}
break;
default:
// treat anything else as a container
pushContainer(qName, attributes);
break;
}
}