public void startElement()

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;
        }
    }