1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.libcore.timezone.telephonylookup; 17 18 import static com.android.libcore.timezone.telephonylookup.TelephonyLookupProtoFileSupport.parseTelephonyLookupTextFile; 19 20 import com.android.libcore.timezone.telephonylookup.proto.TelephonyLookupProtoFile; 21 import com.android.libcore.timezone.util.Errors; 22 import com.android.libcore.timezone.util.Errors.HaltExecutionException; 23 24 import com.ibm.icu.util.ULocale; 25 26 import java.io.IOException; 27 import java.text.ParseException; 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.HashSet; 31 import java.util.List; 32 import java.util.Locale; 33 import java.util.Set; 34 import java.util.stream.Collectors; 35 36 import javax.xml.stream.XMLStreamException; 37 38 /** 39 * Generates the telephonylookup.xml file using the information from telephonylookup.txt. 40 * 41 * See {@link #main(String[])} for commandline information. 42 */ 43 public final class TelephonyLookupGenerator { 44 45 private final String telephonyLookupProtoFile; 46 private final String outputFile; 47 48 /** 49 * Executes the generator. 50 * 51 * Positional arguments: 52 * 1: The telephonylookup.txt proto file 53 * 2: the file to generate 54 */ main(String[] args)55 public static void main(String[] args) throws Exception { 56 if (args.length != 2) { 57 System.err.println( 58 "usage: java " + TelephonyLookupGenerator.class.getName() 59 + " <input proto file> <output xml file>"); 60 System.exit(0); 61 } 62 boolean success = new TelephonyLookupGenerator(args[0], args[1]).execute(); 63 System.exit(success ? 0 : 1); 64 } 65 TelephonyLookupGenerator(String telephonyLookupProtoFile, String outputFile)66 TelephonyLookupGenerator(String telephonyLookupProtoFile, String outputFile) { 67 this.telephonyLookupProtoFile = telephonyLookupProtoFile; 68 this.outputFile = outputFile; 69 } 70 execute()71 boolean execute() throws IOException { 72 Errors errors = new Errors(); 73 try { 74 // Parse the countryzones input file. 75 TelephonyLookupProtoFile.TelephonyLookup telephonyLookupIn; 76 try { 77 telephonyLookupIn = parseTelephonyLookupTextFile(telephonyLookupProtoFile); 78 } catch (ParseException e) { 79 throw errors.addFatalAndHalt("Unable to parse " + telephonyLookupProtoFile, e); 80 } 81 82 List<TelephonyLookupProtoFile.Network> networksIn = telephonyLookupIn.getNetworksList(); 83 84 validateNetworks(networksIn, errors); 85 errors.throwIfError("One or more validation errors encountered"); 86 87 TelephonyLookupXmlFile.TelephonyLookup telephonyLookupOut = 88 createOutputTelephonyLookup(networksIn); 89 logInfo("Writing " + outputFile); 90 try { 91 TelephonyLookupXmlFile.write(telephonyLookupOut, outputFile); 92 } catch (XMLStreamException e) { 93 throw errors.addFatalAndHalt("Unable to write output file", e); 94 } 95 } catch (HaltExecutionException e) { 96 e.printStackTrace(); 97 logError("Stopping due to fatal error: " + e.getMessage()); 98 } finally { 99 // Report all warnings / errors 100 if (!errors.isEmpty()) { 101 logInfo("Issues:\n" + errors.asString()); 102 } 103 } 104 return !errors.hasError(); 105 } 106 validateNetworks(List<TelephonyLookupProtoFile.Network> networksIn, Errors errors)107 private static void validateNetworks(List<TelephonyLookupProtoFile.Network> networksIn, 108 Errors errors) { 109 errors.pushScope("validateNetworks"); 110 try { 111 Set<String> knownIsoCountries = getLowerCaseCountryIsoCodes(); 112 Set<String> mccMncSet = new HashSet<>(); 113 for (TelephonyLookupProtoFile.Network networkIn : networksIn) { 114 String mcc = networkIn.getMcc(); 115 if (mcc.length() != 3 || !isAsciiNumeric(mcc)) { 116 errors.addError("mcc=" + mcc + " must have 3 decimal digits"); 117 } 118 119 String mnc = networkIn.getMnc(); 120 if (!(mnc.length() == 2 || mnc.length() == 3) || !isAsciiNumeric(mnc)) { 121 errors.addError("mnc=" + mnc + " must have 2 or 3 decimal digits"); 122 } 123 124 String mccMnc = "" + mcc + mnc; 125 if (!mccMncSet.add(mccMnc)) { 126 errors.addError("Duplicate entry for mcc=" + mcc + ", mnc=" + mnc); 127 } 128 129 String countryIsoCode = networkIn.getCountryIsoCode(); 130 String countryIsoCodeLower = countryIsoCode.toLowerCase(Locale.ROOT); 131 if (!countryIsoCodeLower.equals(countryIsoCode)) { 132 errors.addError("Country code not lower case: " + countryIsoCode); 133 } 134 135 if (!knownIsoCountries.contains(countryIsoCodeLower)) { 136 errors.addError("Country code not known: " + countryIsoCode); 137 } 138 } 139 } finally { 140 errors.popScope(); 141 } 142 } 143 isAsciiNumeric(String string)144 private static boolean isAsciiNumeric(String string) { 145 for (int i = 0; i < string.length(); i++) { 146 char character = string.charAt(i); 147 if (character < '0' || character > '9') { 148 return false; 149 } 150 } 151 return true; 152 } 153 getLowerCaseCountryIsoCodes()154 private static Set<String> getLowerCaseCountryIsoCodes() { 155 // Use ICU4J's knowledge of ISO codes because we keep that up to date. 156 List<String> knownIsoCountryCodes = Arrays.asList(ULocale.getISOCountries()); 157 knownIsoCountryCodes = knownIsoCountryCodes.stream() 158 .map(x -> x.toLowerCase(Locale.ROOT)) 159 .collect(Collectors.toList()); 160 return new HashSet<>(knownIsoCountryCodes); 161 } 162 createOutputTelephonyLookup( List<TelephonyLookupProtoFile.Network> networksIn)163 private static TelephonyLookupXmlFile.TelephonyLookup createOutputTelephonyLookup( 164 List<TelephonyLookupProtoFile.Network> networksIn) { 165 List<TelephonyLookupXmlFile.Network> networksOut = new ArrayList<>(); 166 for (TelephonyLookupProtoFile.Network networkIn : networksIn) { 167 String mcc = networkIn.getMcc(); 168 String mnc = networkIn.getMnc(); 169 String countryIsoCode = networkIn.getCountryIsoCode(); 170 TelephonyLookupXmlFile.Network networkOut = 171 new TelephonyLookupXmlFile.Network(mcc, mnc, countryIsoCode); 172 networksOut.add(networkOut); 173 } 174 return new TelephonyLookupXmlFile.TelephonyLookup(networksOut); 175 } 176 logError(String msg)177 private static void logError(String msg) { 178 System.err.println("E: " + msg); 179 } 180 logError(String s, Throwable e)181 private static void logError(String s, Throwable e) { 182 logError(s); 183 e.printStackTrace(System.err); 184 } 185 logInfo(String msg)186 private static void logInfo(String msg) { 187 System.err.println("I: " + msg); 188 } 189 } 190