1 /*
   2  * Copyright (c) 2012, 2013, 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 package build.tools.cldrconverter;
  27 
  28 import java.io.File;
  29 import java.io.IOException;
  30 import java.util.HashMap;
  31 import java.util.Map;
  32 import org.xml.sax.Attributes;
  33 import org.xml.sax.InputSource;
  34 import org.xml.sax.SAXException;
  35 
  36 /**
  37  * Handles parsing of files in Locale Data Markup Language for SupplementData.xml
  38  * and produces a map that uses the keys and values of JRE locale data.
  39  */
  40 
  41 class SupplementDataParseHandler extends AbstractLDMLHandler<Object> {
  42     //UNM49 region and composition code used in supplementalData.xml
  43     private static final String WORLD = "001";
  44 
  45     private static final String JAVA_FIRSTDAY = "firstDayOfWeek";
  46     private static final String JAVA_MINDAY = "minimalDaysInFirstWeek";
  47 
  48     // The weekData is now in supplementalData.xml,
  49     // which is not a locale specific file.
  50     // Map for JRE is created locale specific way.
  51     // When parsing the locale neutral file (supplementalData.xml),
  52     // we need to rely on the country code because
  53     // the weekData is listed using country code.
  54     //
  55     // weekData are generated per each country
  56     private final Map<String, Object> firstDayMap;
  57     private final Map<String, Object> minDaysMap;
  58 
  59     // Parent locales. These information will only be
  60     // generated towards the base meta info, with the format of
  61     //
  62     // parentLocale.<parent_locale_id>=<child_locale_id>(" "<child_locale_id>)+
  63     private final Map<String, String> parentLocalesMap;
  64 
  65     SupplementDataParseHandler() {
  66         firstDayMap = new HashMap<>();
  67         minDaysMap = new HashMap<>();
  68         parentLocalesMap = new HashMap<>();
  69     }
  70 
  71     /**
  72      * It returns Map that contains the firstDay and minDays information for
  73      * the country. The Map is created in JRE format after obtaining the data
  74      * from two Maps, firstDayMap and minDaysMap.
  75      *
  76      * It returns null when there is no firstDay and minDays for the country
  77      * although this should not happen because supplementalData.xml includes
  78      * default value for the world ("001") for firstDay and minDays.
  79      */
  80     Map<String, Object> getData(String id) {
  81         Map<String, Object> values = new HashMap<>();
  82         if ("root".equals(id)) {
  83             parentLocalesMap.keySet().forEach(key -> {
  84             values.put(CLDRConverter.PARENT_LOCALE_PREFIX+key,
  85                 parentLocalesMap.get(key));
  86             });
  87         } else {
  88             String countryData = getWeekData(id, JAVA_FIRSTDAY, firstDayMap);
  89             if (countryData != null) {
  90                 values.put(JAVA_FIRSTDAY, countryData);
  91             }
  92             String minDaysData = getWeekData(id, JAVA_MINDAY, minDaysMap);
  93             if (minDaysData != null) {
  94                 values.put(JAVA_MINDAY, minDaysData);
  95             }
  96         }
  97         return values.isEmpty() ? null : values;
  98     }
  99 
 100     /**
 101      * It returns either firstDay or minDays in the JRE format for the country.
 102      *
 103      * @param country       territory code of the requested data
 104      * @param jreDataName   JAVA_FIRSTDAY or JAVA_MINDAY
 105      * @param dataMap       firstDayMap or minDaysMap
 106      * @return the value for the given jreDataName, or null if requested value
 107      *         (firstDay/minDays) is not available although that is highly unlikely
 108      *         because of the default value for the world (001).
 109      */
 110     String getWeekData(String country, final String jreDataName, final Map<String, Object> dataMap) {
 111         String countryValue = null;
 112         String defaultWorldValue = null;
 113         for (String key : dataMap.keySet()) {
 114             if (key.contains(country)) {
 115                 if (jreDataName.equals(JAVA_FIRSTDAY)) {
 116                     countryValue = DAY_OF_WEEK_MAP.get((String) dataMap.get(key));
 117                 } else if (jreDataName.equals(JAVA_MINDAY)) {
 118                     countryValue = (String) dataMap.get(key);
 119                 }
 120                 if (countryValue != null) {
 121                     return countryValue;
 122                 }
 123             } else if (key.contains(WORLD)) {
 124                 if (jreDataName.equals(JAVA_FIRSTDAY)) {
 125                     defaultWorldValue = DAY_OF_WEEK_MAP.get((String) dataMap.get(key));
 126                 } else if (jreDataName.equals(JAVA_MINDAY)) {
 127                     defaultWorldValue = (String) dataMap.get(key);
 128                 }
 129             }
 130         }
 131         return defaultWorldValue;
 132     }
 133 
 134     @Override
 135     public InputSource resolveEntity(String publicID, String systemID) throws IOException, SAXException {
 136         // avoid HTTP traffic to unicode.org
 137         if (systemID.startsWith(CLDRConverter.SPPL_LDML_DTD_SYSTEM_ID)) {
 138             return new InputSource((new File(CLDRConverter.LOCAL_SPPL_LDML_DTD)).toURI().toString());
 139         }
 140         return null;
 141     }
 142 
 143     /**
 144      * JRE requires all the data to be organized by the locale while CLDR 1.4 list
 145      * Calendar related data (weekData)in SupplementalData.xml.
 146      * startElement stores JRE required data into two Maps,
 147      * firstDayMap and minDaysMap.
 148      */
 149     @Override
 150     public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
 151         // elements we need to actively ignore
 152         switch (qName) {
 153         case "firstDay":
 154             if (!isIgnored(attributes)) {
 155                 firstDayMap.put(attributes.getValue("territories"), attributes.getValue("day"));
 156             }
 157             break;
 158         case "minDays":
 159             if (!isIgnored(attributes)) {
 160                 minDaysMap.put(attributes.getValue("territories"), attributes.getValue("count"));
 161             }
 162             break;
 163         case "parentLocale":
 164             if (!isIgnored(attributes)) {
 165                 parentLocalesMap.put(
 166                     attributes.getValue("parent").replaceAll("_", "-"),
 167                     attributes.getValue("locales").replaceAll("_", "-"));
 168             }
 169             break;
 170         default:
 171             // treat anything else as a container
 172             pushContainer(qName, attributes);
 173             break;
 174         }
 175     }
 176 
 177 }