< prev index next >

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

Print this page




  35 import java.util.Locale;
  36 import java.util.Map;
  37 import java.util.Set;
  38 import org.xml.sax.Attributes;
  39 import org.xml.sax.InputSource;
  40 import org.xml.sax.SAXException;
  41 
  42 /**
  43  * Handles parsing of files in Locale Data Markup Language and produces a map
  44  * that uses the keys and values of JRE locale data.
  45  */
  46 class LDMLParseHandler extends AbstractLDMLHandler<Object> {
  47     private String defaultNumberingSystem;
  48     private String currentNumberingSystem = "";
  49     private CalendarType currentCalendarType;
  50     private String zoneNameStyle; // "long" or "short" for time zone names
  51     private String zonePrefix;
  52     private final String id;
  53     private String currentContext = ""; // "format"/"stand-alone"
  54     private String currentWidth = ""; // "wide"/"narrow"/"abbreviated"


  55 
  56     LDMLParseHandler(String id) {
  57         this.id = id;
  58     }
  59 
  60     @Override
  61     public InputSource resolveEntity(String publicID, String systemID) throws IOException, SAXException {
  62         // avoid HTTP traffic to unicode.org
  63         if (systemID.startsWith(CLDRConverter.LDML_DTD_SYSTEM_ID)) {
  64             return new InputSource((new File(CLDRConverter.LOCAL_LDML_DTD)).toURI().toString());
  65         }
  66         return null;
  67     }
  68 
  69     @Override
  70     public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
  71         switch (qName) {
  72         //
  73         // Generic information
  74         //


 486             zoneNameStyle = "long";
 487             pushContainer(qName, attributes);
 488             break;
 489         case "short":
 490             zoneNameStyle = "short";
 491             pushContainer(qName, attributes);
 492             break;
 493         case "generic":  // generic name
 494         case "standard": // standard time name
 495         case "daylight": // daylight saving (summer) time name
 496             pushStringEntry(qName, attributes, CLDRConverter.ZONE_NAME_PREFIX + qName + "." + zoneNameStyle);
 497             break;
 498         case "exemplarCity":
 499             pushStringEntry(qName, attributes, CLDRConverter.EXEMPLAR_CITY_PREFIX);
 500             break;
 501 
 502         //
 503         // Number format information
 504         //
 505         case "decimalFormatLength":
 506             if (attributes.getValue("type") == null) {
 507                 // skipping type="short" data
 508                 // for FormatData
 509                 // copy string for later assembly into NumberPatterns
 510                 pushStringEntry(qName, attributes, "NumberPatterns/decimal");

 511             } else {









 512                 pushIgnoredContainer(qName);































































 513             }
 514             break;
 515         case "currencyFormatLength":
 516             if (attributes.getValue("type") == null) {
 517                 // skipping type="short" data
 518                 // for FormatData
 519                 pushContainer(qName, attributes);
 520             } else {
 521                 pushIgnoredContainer(qName);
 522             }
 523             break;
 524         case "currencyFormat":
 525             // for FormatData
 526             // copy string for later assembly into NumberPatterns
 527             if (attributes.getValue("type").equals("standard")) {
 528             pushStringEntry(qName, attributes, "NumberPatterns/currency");
 529             } else {
 530                 pushIgnoredContainer(qName);
 531             }
 532             break;


 659         case "dateTimeFormatLength":
 660             {
 661                 // for FormatData
 662                 // copy string for later assembly into DateTimePatterns
 663                 String prefix = (currentCalendarType == null) ? "" : currentCalendarType.keyElementName();
 664                 pushStringEntry(qName, attributes, prefix + "DateTimePatterns/" + attributes.getValue("type") + "-dateTime");
 665             }
 666             break;
 667         case "localizedPatternChars":
 668             {
 669                 // for FormatData
 670                 // copy string for later adaptation to JRE use
 671                 String prefix = (currentCalendarType == null) ? "" : currentCalendarType.keyElementName();
 672                 pushStringEntry(qName, attributes, prefix + "DateTimePatternChars");
 673             }
 674             break;
 675 
 676         // "alias" for root
 677         case "alias":
 678             {
 679                 if (id.equals("root") &&
 680                         !isIgnored(attributes) &&
 681                         currentCalendarType != null &&
 682                         !currentCalendarType.lname().startsWith("islamic-")) { // ignore Islamic variants
 683                     pushAliasEntry(qName, attributes, attributes.getValue("path"));
 684                 } else {
 685                     pushIgnoredContainer(qName);
 686                 }
 687             }
 688             break;
 689 
 690         default:
 691             // treat anything else as a container
 692             pushContainer(qName, attributes);
 693             break;
 694         }
 695     }
 696 
 697     private static final String[] CONTEXTS = {"stand-alone", "format"};
 698     private static final String[] WIDTHS = {"wide", "narrow", "abbreviated"};
 699     private static final String[] LENGTHS = {"full", "long", "medium", "short"};
 700 
 701     private void populateWidthAlias(String type, Set<String> keys) {
 702         for (String context : CONTEXTS) {


 814                 type + "-" +
 815                 keyName.substring(0, keyName.indexOf("FormatLength"));
 816             break;
 817         case "eraNames":
 818             keyName = "long.Eras";
 819             break;
 820         case "eraAbbr":
 821             keyName = "Eras";
 822             break;
 823         case "eraNarrow":
 824             keyName = "narrow.Eras";
 825             break;
 826         case "dateFormats":
 827         case "timeFormats":
 828         case "days":
 829         case "months":
 830         case "quarters":
 831         case "dayPeriods":
 832         case "eras":
 833             break;



 834         default:
 835             keyName = "";
 836             break;
 837         }
 838 
 839         return keyName;
 840     }
 841 
 842     private String getTarget(String path, String calType, String context, String width) {
 843         // Target qName
 844         int lastSlash = path.lastIndexOf('/');
 845         String qName = path.substring(lastSlash+1);
 846         int bracket = qName.indexOf('[');
 847         if (bracket != -1) {
 848             qName = qName.substring(0, bracket);
 849         }
 850 
 851         // calType
 852         String typeKey = "/calendar[@type='";
 853         int start = path.indexOf(typeKey);
 854         if (start != -1) {
 855             calType = path.substring(start+typeKey.length(), path.indexOf("']", start));
 856         }
 857 
 858         // context
 859         typeKey = "Context[@type='";
 860         start = path.indexOf(typeKey);
 861         if (start != -1) {
 862             context = (path.substring(start+typeKey.length(), path.indexOf("']", start)));
 863         }
 864 
 865         // width
 866         typeKey = "Width[@type='";
 867         start = path.indexOf(typeKey);
 868         if (start != -1) {
 869             width = path.substring(start+typeKey.length(), path.indexOf("']", start));
 870         }
 871 








 872         return calType + "." + toJDKKey(qName, context, width);
 873     }
 874 
 875     @Override
 876     public void endElement(String uri, String localName, String qName) throws SAXException {
 877         assert qName.equals(currentContainer.getqName()) : "current=" + currentContainer.getqName() + ", param=" + qName;
 878         switch (qName) {
 879         case "calendar":
 880             assert !(currentContainer instanceof Entry);
 881             currentCalendarType = null;
 882             break;
 883 
 884         case "defaultNumberingSystem":
 885             if (currentContainer instanceof StringEntry) {
 886                 defaultNumberingSystem = ((StringEntry) currentContainer).getValue();
 887                 assert defaultNumberingSystem != null;
 888                 put(((StringEntry) currentContainer).getKey(), defaultNumberingSystem);
 889             } else {
 890                 defaultNumberingSystem = null;
 891             }


 909                     valmap.put(entry.getKey(), (String) entry.getValue());
 910                 }
 911             }
 912             break;
 913 
 914         case "monthWidth":
 915         case "dayWidth":
 916         case "dayPeriodWidth":
 917         case "quarterWidth":
 918             currentWidth = "";
 919             putIfEntry();
 920             break;
 921 
 922         case "monthContext":
 923         case "dayContext":
 924         case "dayPeriodContext":
 925         case "quarterContext":
 926             currentContext = "";
 927             putIfEntry();
 928             break;
 929 




 930         default:
 931             putIfEntry();
 932         }
 933         currentContainer = currentContainer.getParent();
 934     }
 935 
 936     private void putIfEntry() {
 937         if (currentContainer instanceof AliasEntry) {
 938             Entry<?> entry = (Entry<?>) currentContainer;
 939             String containerqName = entry.getParent().getqName();





 940             Set<String> keyNames = populateAliasKeys(containerqName, currentContext, currentWidth);
 941             if (!keyNames.isEmpty()) {
 942                 for (String keyName : keyNames) {
 943                     String[] tmp = keyName.split(",", 3);
 944                     String calType = currentCalendarType.lname();
 945                     String src = calType+"."+tmp[0];
 946                     String target = getTarget(
 947                                 entry.getKey(),
 948                                 calType,
 949                                 tmp[1].length()>0 ? tmp[1] : currentContext,
 950                                 tmp[2].length()>0 ? tmp[2] : currentWidth);
 951                     if (target.substring(target.lastIndexOf('.')+1).equals(containerqName)) {
 952                         target = target.substring(0, target.indexOf('.'))+"."+tmp[0];
 953                     }
 954                     CLDRConverter.aliases.put(src.replaceFirst("^gregorian.", ""),
 955                                               target.replaceFirst("^gregorian.", ""));

 956                 }
 957             }
 958         } else if (currentContainer instanceof Entry) {
 959             Entry<?> entry = (Entry<?>) currentContainer;
 960             Object value = entry.getValue();
 961             if (value != null) {
 962                 String key = entry.getKey();
 963                 // Tweak for MonthNames for the root locale, Needed for
 964                 // SimpleDateFormat.format()/parse() roundtrip.
 965                 if (id.equals("root") && key.startsWith("MonthNames")) {
 966                     value = new DateFormatSymbols(Locale.US).getShortMonths();
 967                 }
 968                 put(entry.getKey(), value);
 969             }
 970         }
 971     }
 972 
 973     public String convertOldKeyName(String key) {
 974         // Explicitly obtained from "alias" attribute in each "key" element.
 975         switch (key) {


  35 import java.util.Locale;
  36 import java.util.Map;
  37 import java.util.Set;
  38 import org.xml.sax.Attributes;
  39 import org.xml.sax.InputSource;
  40 import org.xml.sax.SAXException;
  41 
  42 /**
  43  * Handles parsing of files in Locale Data Markup Language and produces a map
  44  * that uses the keys and values of JRE locale data.
  45  */
  46 class LDMLParseHandler extends AbstractLDMLHandler<Object> {
  47     private String defaultNumberingSystem;
  48     private String currentNumberingSystem = "";
  49     private CalendarType currentCalendarType;
  50     private String zoneNameStyle; // "long" or "short" for time zone names
  51     private String zonePrefix;
  52     private final String id;
  53     private String currentContext = ""; // "format"/"stand-alone"
  54     private String currentWidth = ""; // "wide"/"narrow"/"abbreviated"
  55     private String currentStyle = ""; // short, long for decimalFormat
  56     private String compactCount = ""; // one or other for decimalFormat
  57 
  58     LDMLParseHandler(String id) {
  59         this.id = id;
  60     }
  61 
  62     @Override
  63     public InputSource resolveEntity(String publicID, String systemID) throws IOException, SAXException {
  64         // avoid HTTP traffic to unicode.org
  65         if (systemID.startsWith(CLDRConverter.LDML_DTD_SYSTEM_ID)) {
  66             return new InputSource((new File(CLDRConverter.LOCAL_LDML_DTD)).toURI().toString());
  67         }
  68         return null;
  69     }
  70 
  71     @Override
  72     public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
  73         switch (qName) {
  74         //
  75         // Generic information
  76         //


 488             zoneNameStyle = "long";
 489             pushContainer(qName, attributes);
 490             break;
 491         case "short":
 492             zoneNameStyle = "short";
 493             pushContainer(qName, attributes);
 494             break;
 495         case "generic":  // generic name
 496         case "standard": // standard time name
 497         case "daylight": // daylight saving (summer) time name
 498             pushStringEntry(qName, attributes, CLDRConverter.ZONE_NAME_PREFIX + qName + "." + zoneNameStyle);
 499             break;
 500         case "exemplarCity":
 501             pushStringEntry(qName, attributes, CLDRConverter.EXEMPLAR_CITY_PREFIX);
 502             break;
 503 
 504         //
 505         // Number format information
 506         //
 507         case "decimalFormatLength":
 508             String type = attributes.getValue("type");
 509             if (null == type) {
 510                 // format data for decimal number format

 511                 pushStringEntry(qName, attributes, "NumberPatterns/decimal");
 512                 currentStyle = type;
 513             } else {
 514                 switch (type) {
 515                     case "short":
 516                     case "long":
 517                         // considering "short" and long for
 518                         // compact number formatting patterns
 519                         pushKeyContainer(qName, attributes, type);
 520                         currentStyle = type;
 521                         break;
 522                     default:
 523                         pushIgnoredContainer(qName);
 524                         break;
 525                 }
 526             }
 527             break;
 528         case "decimalFormat":
 529             if(currentStyle == null) {
 530                 pushContainer(qName, attributes);
 531             } else {
 532                 switch (currentStyle) {
 533                     case "short":
 534                         pushStringListEntry(qName, attributes,
 535                                 currentStyle+".CompactNumberPatterns");
 536                         break;
 537                     case "long":
 538                         pushStringListEntry(qName, attributes,
 539                                 currentStyle+".CompactNumberPatterns");
 540                         break;
 541                     default:
 542                         pushIgnoredContainer(qName);
 543                         break;
 544                 }
 545             }
 546             break;
 547         case "pattern":
 548             String containerName = currentContainer.getqName();
 549             if (containerName.equals("decimalFormat")) {
 550                 if (currentStyle == null) {
 551                     pushContainer(qName, attributes);
 552                 } else {
 553                     // The compact number patterns parsing assumes that the order
 554                     // of patterns are always in the increasing order of their
 555                     // type attribute i.e. type = 1000...
 556                     // Between the inflectional forms for a type (e.g.
 557                     // count = "one" and count = "other" for type = 1000), it is
 558                     // assumed that the count = "one" always appears before
 559                     // count = "other"
 560                     switch (currentStyle) {
 561                         case "short":
 562                         case "long":
 563                             String count = attributes.getValue("count");
 564                             // first pattern of count = "one" or count = "other"
 565                             if ((count.equals("one") || count.equals("other"))
 566                                     && compactCount.equals("")) {
 567                                 compactCount = count;
 568                                 pushStringListElement(qName, attributes,
 569                                         (int) Math.log10(Double.parseDouble(attributes.getValue("type"))));
 570                             } else if ((count.equals("one") || count.equals("other"))
 571                                     && compactCount.equals(count)) {
 572                                 // extract patterns with similar "count"
 573                                 // attribute value
 574                                 pushStringListElement(qName, attributes,
 575                                         (int) Math.log10(Double.parseDouble(attributes.getValue("type"))));
 576                             } else {
 577                                 pushIgnoredContainer(qName);
 578                             }
 579                             break;
 580                         default:
 581                             pushIgnoredContainer(qName);
 582                             break;
 583                     }
 584                 }
 585             } else {
 586                 pushContainer(qName, attributes);
 587             }
 588             break;
 589         case "currencyFormatLength":
 590             if (attributes.getValue("type") == null) {
 591                 // skipping type="short" data
 592                 // for FormatData
 593                 pushContainer(qName, attributes);
 594             } else {
 595                 pushIgnoredContainer(qName);
 596             }
 597             break;
 598         case "currencyFormat":
 599             // for FormatData
 600             // copy string for later assembly into NumberPatterns
 601             if (attributes.getValue("type").equals("standard")) {
 602             pushStringEntry(qName, attributes, "NumberPatterns/currency");
 603             } else {
 604                 pushIgnoredContainer(qName);
 605             }
 606             break;


 733         case "dateTimeFormatLength":
 734             {
 735                 // for FormatData
 736                 // copy string for later assembly into DateTimePatterns
 737                 String prefix = (currentCalendarType == null) ? "" : currentCalendarType.keyElementName();
 738                 pushStringEntry(qName, attributes, prefix + "DateTimePatterns/" + attributes.getValue("type") + "-dateTime");
 739             }
 740             break;
 741         case "localizedPatternChars":
 742             {
 743                 // for FormatData
 744                 // copy string for later adaptation to JRE use
 745                 String prefix = (currentCalendarType == null) ? "" : currentCalendarType.keyElementName();
 746                 pushStringEntry(qName, attributes, prefix + "DateTimePatternChars");
 747             }
 748             break;
 749 
 750         // "alias" for root
 751         case "alias":
 752             {
 753                 if (id.equals("root") && !isIgnored(attributes)
 754                         && ((currentContainer.getqName().equals("decimalFormatLength"))
 755                         || (currentCalendarType != null && !currentCalendarType.lname().startsWith("islamic-")))) { // ignore islamic variants

 756                     pushAliasEntry(qName, attributes, attributes.getValue("path"));
 757                 } else {
 758                     pushIgnoredContainer(qName);
 759                 }
 760             }
 761             break;
 762 
 763         default:
 764             // treat anything else as a container
 765             pushContainer(qName, attributes);
 766             break;
 767         }
 768     }
 769 
 770     private static final String[] CONTEXTS = {"stand-alone", "format"};
 771     private static final String[] WIDTHS = {"wide", "narrow", "abbreviated"};
 772     private static final String[] LENGTHS = {"full", "long", "medium", "short"};
 773 
 774     private void populateWidthAlias(String type, Set<String> keys) {
 775         for (String context : CONTEXTS) {


 887                 type + "-" +
 888                 keyName.substring(0, keyName.indexOf("FormatLength"));
 889             break;
 890         case "eraNames":
 891             keyName = "long.Eras";
 892             break;
 893         case "eraAbbr":
 894             keyName = "Eras";
 895             break;
 896         case "eraNarrow":
 897             keyName = "narrow.Eras";
 898             break;
 899         case "dateFormats":
 900         case "timeFormats":
 901         case "days":
 902         case "months":
 903         case "quarters":
 904         case "dayPeriods":
 905         case "eras":
 906             break;
 907         case "decimalFormatLength": // used for compact number formatting patterns
 908             keyName = type + ".CompactNumberPatterns";
 909             break;
 910         default:
 911             keyName = "";
 912             break;
 913         }
 914 
 915         return keyName;
 916     }
 917 
 918     private String getTarget(String path, String calType, String context, String width) {
 919         // Target qName
 920         int lastSlash = path.lastIndexOf('/');
 921         String qName = path.substring(lastSlash+1);
 922         int bracket = qName.indexOf('[');
 923         if (bracket != -1) {
 924             qName = qName.substring(0, bracket);
 925         }
 926 
 927         // calType
 928         String typeKey = "/calendar[@type='";
 929         int start = path.indexOf(typeKey);
 930         if (start != -1) {
 931             calType = path.substring(start+typeKey.length(), path.indexOf("']", start));
 932         }
 933 
 934         // context
 935         typeKey = "Context[@type='";
 936         start = path.indexOf(typeKey);
 937         if (start != -1) {
 938             context = (path.substring(start+typeKey.length(), path.indexOf("']", start)));
 939         }
 940 
 941         // width
 942         typeKey = "Width[@type='";
 943         start = path.indexOf(typeKey);
 944         if (start != -1) {
 945             width = path.substring(start+typeKey.length(), path.indexOf("']", start));
 946         }
 947 
 948         // used for compact number formatting patterns aliases
 949         typeKey = "decimalFormatLength[@type='";
 950         start = path.indexOf(typeKey);
 951         if (start != -1) {
 952             String style = path.substring(start + typeKey.length(), path.indexOf("']", start));
 953             return toJDKKey(qName, "", style);
 954         }
 955 
 956         return calType + "." + toJDKKey(qName, context, width);
 957     }
 958 
 959     @Override
 960     public void endElement(String uri, String localName, String qName) throws SAXException {
 961         assert qName.equals(currentContainer.getqName()) : "current=" + currentContainer.getqName() + ", param=" + qName;
 962         switch (qName) {
 963         case "calendar":
 964             assert !(currentContainer instanceof Entry);
 965             currentCalendarType = null;
 966             break;
 967 
 968         case "defaultNumberingSystem":
 969             if (currentContainer instanceof StringEntry) {
 970                 defaultNumberingSystem = ((StringEntry) currentContainer).getValue();
 971                 assert defaultNumberingSystem != null;
 972                 put(((StringEntry) currentContainer).getKey(), defaultNumberingSystem);
 973             } else {
 974                 defaultNumberingSystem = null;
 975             }


 993                     valmap.put(entry.getKey(), (String) entry.getValue());
 994                 }
 995             }
 996             break;
 997 
 998         case "monthWidth":
 999         case "dayWidth":
1000         case "dayPeriodWidth":
1001         case "quarterWidth":
1002             currentWidth = "";
1003             putIfEntry();
1004             break;
1005 
1006         case "monthContext":
1007         case "dayContext":
1008         case "dayPeriodContext":
1009         case "quarterContext":
1010             currentContext = "";
1011             putIfEntry();
1012             break;
1013         case "decimalFormatLength":
1014             currentStyle = "";
1015             compactCount = "";
1016             putIfEntry();
1017             break;
1018         default:
1019             putIfEntry();
1020         }
1021         currentContainer = currentContainer.getParent();
1022     }
1023 
1024     private void putIfEntry() {
1025         if (currentContainer instanceof AliasEntry) {
1026             Entry<?> entry = (Entry<?>) currentContainer;
1027             String containerqName = entry.getParent().getqName();
1028             if (containerqName.equals("decimalFormatLength")) {
1029                 String srcKey = toJDKKey(containerqName, "", currentStyle);
1030                 String targetKey = getTarget(entry.getKey(), "", "", "");
1031                 CLDRConverter.aliases.put(srcKey, targetKey);
1032             } else {
1033                 Set<String> keyNames = populateAliasKeys(containerqName, currentContext, currentWidth);
1034                 if (!keyNames.isEmpty()) {
1035                     for (String keyName : keyNames) {
1036                         String[] tmp = keyName.split(",", 3);
1037                         String calType = currentCalendarType.lname();
1038                         String src = calType+"."+tmp[0];
1039                         String target = getTarget(
1040                                     entry.getKey(),
1041                                     calType,
1042                                     tmp[1].length()>0 ? tmp[1] : currentContext,
1043                                     tmp[2].length()>0 ? tmp[2] : currentWidth);
1044                         if (target.substring(target.lastIndexOf('.')+1).equals(containerqName)) {
1045                             target = target.substring(0, target.indexOf('.'))+"."+tmp[0];
1046                         }
1047                         CLDRConverter.aliases.put(src.replaceFirst("^gregorian.", ""),
1048                                                   target.replaceFirst("^gregorian.", ""));
1049                     }
1050                 }
1051             }
1052         } else if (currentContainer instanceof Entry) {
1053             Entry<?> entry = (Entry<?>) currentContainer;
1054             Object value = entry.getValue();
1055             if (value != null) {
1056                 String key = entry.getKey();
1057                 // Tweak for MonthNames for the root locale, Needed for
1058                 // SimpleDateFormat.format()/parse() roundtrip.
1059                 if (id.equals("root") && key.startsWith("MonthNames")) {
1060                     value = new DateFormatSymbols(Locale.US).getShortMonths();
1061                 }
1062                 put(entry.getKey(), value);
1063             }
1064         }
1065     }
1066 
1067     public String convertOldKeyName(String key) {
1068         // Explicitly obtained from "alias" attribute in each "key" element.
1069         switch (key) {
< prev index next >