make/src/classes/build/tools/cldrconverter/LDMLParseHandler.java

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -27,13 +27,15 @@
 
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
 import org.xml.sax.Attributes;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
 
 /**

@@ -45,10 +47,12 @@
     private String currentNumberingSystem = "";
     private CalendarType currentCalendarType;
     private String zoneNameStyle; // "long" or "short" for time zone names
     private String zonePrefix;
     private final String id;
+    private String currentContext = ""; // "format"/"stand-alone"
+    private String currentWidth = ""; // "wide"/"narrow"/"abbreviated"
 
     LDMLParseHandler(String id) {
         this.id = id;
     }
 

@@ -110,13 +114,14 @@
             break;
 
         // Calendar or currency
         case "displayName":
             {
-                if (currentCalendarType != null) {
+                if (currentContainer.getqName().equals("field")) {
                     pushStringEntry(qName, attributes,
-                            currentCalendarType.keyElementName() + "field." + getContainerKey());
+                            (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();

@@ -147,14 +152,12 @@
                     pushIgnoredContainer(qName);
                 }
             }
             break;
         case "fields":
-            if (currentCalendarType != null) {
+            {
                 pushContainer(qName, attributes);
-            } else {
-                pushIgnoredContainer(qName);
             }
             break;
         case "field":
             {
                 String type = attributes.getValue("type");

@@ -181,10 +184,11 @@
             {
                 // for FormatData
                 // need to keep stand-alone and format, to allow for inheritance in CLDR
                 String type = attributes.getValue("type");
                 if ("stand-alone".equals(type) || "format".equals(type)) {
+                    currentContext = type;
                     pushKeyContainer(qName, attributes, type);
                 } else {
                     pushIgnoredContainer(qName);
                 }
             }

@@ -192,12 +196,17 @@
         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;
+                }
                 String prefix = (currentCalendarType == null) ? "" : currentCalendarType.keyElementName();
-                switch (attributes.getValue("type")) {
+                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);

@@ -220,10 +229,11 @@
             {
                 // for FormatData
                 // need to keep stand-alone and format, to allow for multiple inheritance in CLDR
                 String type = attributes.getValue("type");
                 if ("stand-alone".equals(type) || "format".equals(type)) {
+                    currentContext = type;
                     pushKeyContainer(qName, attributes, type);
                 } else {
                     pushIgnoredContainer(qName);
                 }
             }

@@ -232,11 +242,12 @@
             {
                 // 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
                 String prefix = (currentCalendarType == null) ? "" : currentCalendarType.keyElementName();
-                switch (attributes.getValue("type")) {
+                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);

@@ -261,20 +272,22 @@
             // for FormatData
             // need to keep stand-alone and format, to allow for multiple inheritance in CLDR
             {
                 String 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. only keeping wide
-            switch (attributes.getValue("type")) {
+            currentWidth = attributes.getValue("type");
+            switch (currentWidth) {
             case "wide":
                 pushStringArrayEntry(qName, attributes, "AmPmMarkers/" + getContainerKey(), 2);
                 break;
             case "narrow":
                 pushStringArrayEntry(qName, attributes, "narrow.AmPmMarkers/" + getContainerKey(), 2);

@@ -360,10 +373,11 @@
             {
                 // for FormatData
                 // need to keep stand-alone and format, to allow for inheritance in CLDR
                 String type = attributes.getValue("type");
                 if ("stand-alone".equals(type) || "format".equals(type)) {
+                    currentContext = type;
                     pushKeyContainer(qName, attributes, type);
                 } else {
                     pushIgnoredContainer(qName);
                 }
             }

@@ -371,11 +385,12 @@
         case "quarterWidth":
             {
                 // for FormatData
                 // keep info about the context type so we can sort out inheritance later
                 String prefix = (currentCalendarType == null) ? "" : currentCalendarType.keyElementName();
-                switch (attributes.getValue("type")) {
+                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);

@@ -448,16 +463,24 @@
             }
             break;
         case "currencyFormat":
             // for FormatData
             // copy string for later assembly into NumberPatterns
+            if (attributes.getValue("type").equals("standard")) {
             pushStringEntry(qName, attributes, "NumberPatterns/currency");
+            } else {
+                pushIgnoredContainer(qName);
+            }
             break;
         case "percentFormat":
             // for FormatData
             // copy string for later assembly into NumberPatterns
+            if (attributes.getValue("type").equals("standard")) {
             pushStringEntry(qName, attributes, "NumberPatterns/percent");
+            } else {
+                pushIgnoredContainer(qName);
+            }
             break;
         case "defaultNumberingSystem":
             // default numbering system if multiple numbering systems are used.
             pushStringEntry(qName, attributes, "DefaultNumberingSystem");
             break;

@@ -580,16 +603,16 @@
                 // copy string for later assembly into DateTimePatterns
                 String prefix = (currentCalendarType == null) ? "" : currentCalendarType.keyElementName();
                 pushStringEntry(qName, attributes, prefix + "DateTimePatterns/" + attributes.getValue("type") + "-date");
             }
             break;
-        case "dateTimeFormat":
+        case "dateTimeFormatLength":
             {
                 // for FormatData
                 // copy string for later assembly into DateTimePatterns
                 String prefix = (currentCalendarType == null) ? "" : currentCalendarType.keyElementName();
-                pushStringEntry(qName, attributes, prefix + "DateTimePatterns/date-time");
+                pushStringEntry(qName, attributes, prefix + "DateTimePatterns/" + attributes.getValue("type") + "-dateTime");
             }
             break;
         case "localizedPatternChars":
             {
                 // for FormatData

@@ -597,17 +620,209 @@
                 String prefix = (currentCalendarType == null) ? "" : currentCalendarType.keyElementName();
                 pushStringEntry(qName, attributes, prefix + "DateTimePatternChars");
             }
             break;
 
+        // "alias" for root
+        case "alias":
+            {
+                if (id.equals("root") &&
+                        !isIgnored(attributes) &&
+                        currentCalendarType != null &&
+                        !currentCalendarType.lname().startsWith("islamic-")) { // ignore Islamic variants
+                    pushAliasEntry(qName, attributes, attributes.getValue("path"));
+                } else {
+                    pushIgnoredContainer(qName);
+                }
+            }
+            break;
+
         default:
             // treat anything else as a container
             pushContainer(qName, attributes);
             break;
         }
     }
 
+    private static final String[] CONTEXTS = {"stand-alone", "format"};
+    private static final String[] WIDTHS = {"wide", "narrow", "abbreviated"};
+    private static final String[] LENGTHS = {"full", "long", "medium", "short"};
+
+    private void populateWidthAlias(String type, Set<String> keys) {
+        for (String context : CONTEXTS) {
+            for (String width : WIDTHS) {
+                String keyName = toJDKKey(type+"Width", context, width);
+                if (keyName.length() > 0) {
+                    keys.add(keyName + "," + context + "," + width);
+                }
+            }
+        }
+    }
+
+    private void populateFormatLengthAlias(String type, Set<String> keys) {
+        for (String length: LENGTHS) {
+            String keyName = toJDKKey(type+"FormatLength", currentContext, length);
+            if (keyName.length() > 0) {
+                keys.add(keyName + "," + currentContext + "," + length);
+            }
+        }
+    }
+
+    private Set<String> populateAliasKeys(String qName, String context, String width) {
+        HashSet<String> ret = new HashSet<>();
+        String keyName = qName;
+
+        switch (qName) {
+        case "monthWidth":
+        case "dayWidth":
+        case "quarterWidth":
+        case "dayPeriodWidth":
+        case "dateFormatLength":
+        case "timeFormatLength":
+        case "dateTimeFormatLength":
+        case "eraNames":
+        case "eraAbbr":
+        case "eraNarrow":
+            ret.add(toJDKKey(qName, context, width) + "," + context + "," + width);
+            break;
+        case "days":
+            populateWidthAlias("day", ret);
+            break;
+        case "months":
+            populateWidthAlias("month", ret);
+            break;
+        case "quarters":
+            populateWidthAlias("quarter", ret);
+            break;
+        case "dayPeriods":
+            populateWidthAlias("dayPeriod", ret);
+            break;
+        case "eras":
+            ret.add(toJDKKey("eraNames", context, width) + "," + context + "," + width);
+            ret.add(toJDKKey("eraAbbr", context, width) + "," + context + "," + width);
+            ret.add(toJDKKey("eraNarrow", context, width) + "," + context + "," + width);
+            break;
+        case "dateFormats":
+            populateFormatLengthAlias("date", ret);
+            break;
+        case "timeFormats":
+            populateFormatLengthAlias("time", ret);
+            break;
+        default:
+            break;
+        }
+        return ret;
+    }
+
+    private String translateWidthAlias(String qName, String context, String width) {
+        String keyName = qName;
+        String type = Character.toUpperCase(qName.charAt(0)) + qName.substring(1, qName.indexOf("Width"));
+
+        switch (width) {
+        case "wide":
+            keyName = type + "Names/" + context;
+            break;
+        case "abbreviated":
+            keyName = type + "Abbreviations/" + context;
+            break;
+        case "narrow":
+            keyName = type + "Narrows/" + context;
+            break;
+        default:
+            assert false;
+        }
+
+        return keyName;
+    }
+
+    private String toJDKKey(String containerqName, String context, String type) {
+        String keyName = containerqName;
+
+        switch (containerqName) {
+        case "monthWidth":
+        case "dayWidth":
+        case "quarterWidth":
+            keyName = translateWidthAlias(keyName, context, type);
+            break;
+        case "dayPeriodWidth":
+            switch (type) {
+            case "wide":
+                keyName = "AmPmMarkers/" + context;
+                break;
+            case "narrow":
+                keyName = "narrow.AmPmMarkers/" + context;
+                break;
+            case "abbreviated":
+                keyName = "";
+                break;
+            }
+            break;
+        case "dateFormatLength":
+        case "timeFormatLength":
+        case "dateTimeFormatLength":
+            keyName = "DateTimePatterns/" +
+                type + "-" +
+                keyName.substring(0, keyName.indexOf("FormatLength"));
+            break;
+        case "eraNames":
+            keyName = "long.Eras";
+            break;
+        case "eraAbbr":
+            keyName = "Eras";
+            break;
+        case "eraNarrow":
+            keyName = "narrow.Eras";
+            break;
+        case "dateFormats":
+        case "timeFormats":
+        case "days":
+        case "months":
+        case "quarters":
+        case "dayPeriods":
+        case "eras":
+            break;
+        default:
+            keyName = "";
+            break;
+        }
+
+        return keyName;
+    }
+
+    private String getTarget(String qName, String path, String calType, String context, String width) {
+        // qName
+        int lastSlash = path.lastIndexOf('/');
+        qName = path.substring(lastSlash+1);
+        int bracket = qName.indexOf('[');
+        if (bracket != -1) {
+            qName = qName.substring(0, bracket);
+        }
+
+        // calType
+        String typeKey = "/calendar[@type='";
+        int start = path.indexOf(typeKey);
+        if (start != -1) {
+            calType = path.substring(start+typeKey.length(), path.indexOf("']", start));
+        }
+
+        // context
+        typeKey = "Context[@type='";
+        start = path.indexOf(typeKey);
+        if (start != -1) {
+            context = (path.substring(start+typeKey.length(), path.indexOf("']", start)));
+        }
+
+        // width
+        typeKey = "Width[@type='";
+        start = path.indexOf(typeKey);
+        if (start != -1) {
+            width = path.substring(start+typeKey.length(), path.indexOf("']", start));
+        }
+
+        return calType + "." + toJDKKey(qName, context, width);
+    }
+
     @Override
     public void endElement(String uri, String localName, String qName) throws SAXException {
         assert qName.equals(currentContainer.getqName()) : "current=" + currentContainer.getqName() + ", param=" + qName;
         switch (qName) {
         case "calendar":

@@ -626,27 +841,71 @@
             break;
 
         case "timeZoneNames":
             zonePrefix = null;
             break;
+
         case "generic":
         case "standard":
         case "daylight":
             if (zonePrefix != null && (currentContainer instanceof Entry)) {
                 @SuppressWarnings("unchecked")
                 Map<String, String> valmap = (Map<String, String>) get(zonePrefix + getContainerKey());
                 Entry<?> entry = (Entry<?>) currentContainer;
                 valmap.put(entry.getKey(), (String) entry.getValue());
             }
             break;
+
+        case "monthWidth":
+        case "dayWidth":
+        case "dayPeriodWidth":
+        case "quarterWidth":
+            currentWidth = "";
+            putIfEntry();
+            break;
+
+        case "monthContext":
+        case "dayContext":
+        case "dayPeriodContext":
+        case "quarterContext":
+            currentContext = "";
+            putIfEntry();
+            break;
+
         default:
-            if (currentContainer instanceof Entry) {
+            putIfEntry();
+        }
+        currentContainer = currentContainer.getParent();
+    }
+
+    private void putIfEntry() {
+        if (currentContainer instanceof AliasEntry) {
+            Entry<?> entry = (Entry<?>) currentContainer;
+            String containerqName = entry.getParent().getqName();
+            Set<String> keyNames = populateAliasKeys(containerqName, currentContext, currentWidth);
+            if (!keyNames.isEmpty()) {
+                for (String keyName : keyNames) {
+                    String[] tmp = keyName.split(",", 3);
+                    String calType = currentCalendarType.lname();
+                    String src = calType+"."+tmp[0];
+                    String target = getTarget(containerqName,
+                                entry.getKey(),
+                                calType,
+                                tmp[1].length()>0 ? tmp[1] : currentContext,
+                                tmp[2].length()>0 ? tmp[2] : currentWidth);
+                    if (target.substring(target.lastIndexOf('.')+1).equals(containerqName)) {
+                        // may not always be correct
+                        target = target.substring(0, target.indexOf('.'))+"."+tmp[0];
+                    }
+                    CLDRConverter.aliases.put(src.replaceFirst("^gregorian.", ""),
+                                              target.replaceFirst("^gregorian.", ""));
+                }
+            }
+        } else if (currentContainer instanceof Entry) {
                 Entry<?> entry = (Entry<?>) currentContainer;
                 Object value = entry.getValue();
                 if (value != null) {
                     put(entry.getKey(), value);
                 }
             }
         }
-        currentContainer = currentContainer.getParent();
-    }
 }