1 /*
   2  * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 import java.io.*;
  27 import java.nio.charset.*;
  28 import java.nio.file.*;
  29 import java.util.*;
  30 
  31 public class EquivMapsGenerator {
  32 
  33     /*
  34      * IANA Language Subtag Registry file downloaded from
  35      *     http://www.iana.org/assignments/language-subtag-registry
  36      */
  37     private static final String DEFAULT_LSR_FILE =
  38         "language-subtag-registry.txt";
  39 
  40     private static boolean verbose = false;
  41 
  42     public static void main(String[] args) throws Exception {
  43         String fileLSR = DEFAULT_LSR_FILE;
  44 
  45         for (int i = 0; i < args.length; i++) {
  46             String s = args[i];
  47             if (s.equals("-lsr")) {
  48                 fileLSR = args[++i];
  49             } else if (s.equals("-verbose")) {
  50                 verbose = true;
  51             }
  52         }
  53 
  54         readLSRfile(fileLSR);
  55         generateEquivalentMap();
  56         generateSourceCode();
  57     }
  58 
  59     private static String LSRrevisionDate;
  60     private static Map<String, StringBuilder> initialLanguageMap =
  61         new TreeMap<>();
  62     private static Map<String, StringBuilder> initialRegionVariantMap =
  63         new TreeMap<>();
  64 
  65     private static Map<String, String> sortedLanguageMap1 = new TreeMap<>();
  66     private static Map<String, String[]> sortedLanguageMap2 = new TreeMap<>();
  67     private static Map<String, String> sortedRegionVariantMap =
  68         new TreeMap<>();
  69 
  70     private static void readLSRfile(String filename) throws Exception {
  71         String type = null;
  72         String tag = null;
  73         String preferred = null;
  74         int mappingNum = 0;
  75 
  76         for (String line : Files.readAllLines(Paths.get(filename),
  77                                               Charset.forName("UTF-8"))) {
  78             line = line.toLowerCase();
  79             int index = line.indexOf(' ')+1;
  80             if (line.startsWith("file-date:")) {
  81                 LSRrevisionDate = line.substring(index);
  82                 if (verbose) {
  83                     System.out.println("LSR revision date=" + LSRrevisionDate);
  84                 }
  85             } else if (line.startsWith("type:")) {
  86                 type = line.substring(index);
  87             } else if (line.startsWith("tag:") || line.startsWith("subtag:")) {
  88                 tag = line.substring(index);
  89             } else if (line.startsWith("preferred-value:")
  90                        && !type.equals("extlang")) {
  91                 preferred = line.substring(index);
  92                 mappingNum++;
  93                 processDeprecatedData(type, tag, preferred);
  94             } else if (line.equals("%%")) {
  95                 type = null;
  96                 tag = null;
  97                 preferred = null;
  98             }
  99         }
 100 
 101         if (verbose) {
 102             System.out.println("readLSRfile(" + filename + ")");
 103             System.out.println("  Total number of mapping=" + mappingNum);
 104             System.out.println("\n  Map for language. Size="
 105                 + initialLanguageMap.size());
 106 
 107             for (String key : initialLanguageMap.keySet()) {
 108                 System.out.println("    " + key + ": \""
 109                     + initialLanguageMap.get(key) + "\"");
 110             }
 111 
 112             System.out.println("\n  Map for region and variant. Size="
 113                 + initialRegionVariantMap.size());
 114 
 115             for (String key : initialRegionVariantMap.keySet()) {
 116                 System.out.println("    " + key + ": \""
 117                     + initialRegionVariantMap.get(key) + "\"");
 118             }
 119         }
 120     }
 121 
 122     private static void processDeprecatedData(String type,
 123                                               String tag,
 124                                               String preferred) {
 125         StringBuilder sb;
 126         if (type.equals("region") || type.equals("variant")) {
 127             if (!initialRegionVariantMap.containsKey(preferred)) {
 128                 sb = new StringBuilder("-");
 129                 sb.append(preferred);
 130                 sb.append(",-");
 131                 sb.append(tag);
 132                 initialRegionVariantMap.put("-"+preferred, sb);
 133             } else {
 134                 throw new RuntimeException("New case, need implementation."
 135                     + " A region/variant subtag \"" + preferred
 136                     + "\" is registered for more than one subtags.");
 137             }
 138         } else { // language, grandfahered, and redundant
 139             if (!initialLanguageMap.containsKey(preferred)) {
 140                 sb = new StringBuilder(preferred);
 141                 sb.append(',');
 142                 sb.append(tag);
 143                 initialLanguageMap.put(preferred, sb);
 144             } else {
 145                 sb = initialLanguageMap.get(preferred);
 146                 sb.append(',');
 147                 sb.append(tag);
 148                 initialLanguageMap.put(preferred, sb);
 149             }
 150         }
 151     }
 152 
 153     private static void generateEquivalentMap() {
 154         String[] subtags;
 155         for (String preferred : initialLanguageMap.keySet()) {
 156             subtags = initialLanguageMap.get(preferred).toString().split(",");
 157 
 158             if (subtags.length == 2) {
 159                 sortedLanguageMap1.put(subtags[0], subtags[1]);
 160                 sortedLanguageMap1.put(subtags[1], subtags[0]);
 161             } else if (subtags.length == 3) {
 162                 sortedLanguageMap2.put(subtags[0],
 163                                      new String[]{subtags[1], subtags[2]});
 164                 sortedLanguageMap2.put(subtags[1],
 165                                      new String[]{subtags[0], subtags[2]});
 166                 sortedLanguageMap2.put(subtags[2],
 167                                      new String[]{subtags[0], subtags[1]});
 168             } else {
 169                     throw new RuntimeException("New case, need implementation."
 170                         + " A language subtag \"" + preferred
 171                         + "\" is registered for more than two subtags. ");
 172             }
 173         }
 174 
 175         for (String preferred : initialRegionVariantMap.keySet()) {
 176             subtags =
 177                 initialRegionVariantMap.get(preferred).toString().split(",");
 178 
 179             sortedRegionVariantMap.put(subtags[0], subtags[1]);
 180             sortedRegionVariantMap.put(subtags[1], subtags[0]);
 181         }
 182 
 183         if (verbose) {
 184             System.out.println("generateEquivalentMap()");
 185             System.out.println("  \nSorted map for language subtags which have only one equivalent. Size="
 186                 + sortedLanguageMap1.size());
 187             for (String key : sortedLanguageMap1.keySet()) {
 188                 System.out.println("    " + key + ": \""
 189                     + sortedLanguageMap1.get(key) + "\"");
 190             }
 191 
 192             System.out.println("\n  Sorted map for language subtags which have multiple equivalents. Size="
 193                 + sortedLanguageMap2.size());
 194             for (String key : sortedLanguageMap2.keySet()) {
 195                 String[] s = sortedLanguageMap2.get(key);
 196                 System.out.println("    " + key + ": \""
 197                     + s[0] + "\", \"" + s[1] + "\"");
 198             }
 199 
 200             System.out.println("\n  Sorted map for region and variant subtags. Size="
 201                 + sortedRegionVariantMap.size());
 202             for (String key : sortedRegionVariantMap.keySet()) {
 203                 System.out.println("    " + key + ": \""
 204                     + sortedRegionVariantMap.get(key) + "\"");
 205             }
 206         }
 207         System.out.println();
 208     }
 209 
 210     private final static String headerText =
 211         "final class LocaleEquivalentMaps {\n\n"
 212         + "    static final Map<String, String> singleEquivMap;\n"
 213         + "    static final Map<String, String[]> multiEquivsMap;\n"
 214         + "    static final Map<String, String> regionVariantEquivMap;\n\n"
 215         + "    static {\n"
 216         + "        singleEquivMap = new HashMap<>();\n"
 217         + "        multiEquivsMap = new HashMap<>();\n"
 218         + "        regionVariantEquivMap = new HashMap<>();\n\n"
 219         + "        // This is an auto-generated file and should not be manually edited.\n";
 220 
 221     private final static String footerText =
 222         "    }\n\n"
 223         + "}";
 224 
 225     private static void generateSourceCode() {
 226         System.out.println(headerText
 227             + "        //   LSR Revision: " + LSRrevisionDate);
 228 
 229         for (String key : sortedLanguageMap1.keySet()) {
 230             String value = sortedLanguageMap1.get(key);
 231             System.out.println("        singleEquivMap.put(\""
 232                 + key + "\", \"" + value + "\");");
 233         }
 234         System.out.println();
 235         for (String key : sortedLanguageMap2.keySet()) {
 236             String[] values = sortedLanguageMap2.get(key);
 237             System.out.println("        multiEquivsMap.put(\""
 238                 + key + "\", new String[] {\"" + values[0] + "\", \""
 239                 + values[1] + "\"});");
 240         }
 241         System.out.println();
 242         for (String key : sortedRegionVariantMap.keySet()) {
 243             String value = sortedRegionVariantMap.get(key);
 244             System.out.println("        regionVariantEquivMap.put(\""
 245                 + key + "\", \"" + value + "\");");
 246         }
 247 
 248         System.out.println(footerText);
 249     }
 250 
 251 }