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 java.io.FileOutputStream; 19 import java.io.IOException; 20 import java.io.OutputStreamWriter; 21 import java.io.StringReader; 22 import java.io.StringWriter; 23 import java.io.Writer; 24 import java.nio.charset.StandardCharsets; 25 import java.util.List; 26 import java.util.Objects; 27 28 import javax.xml.stream.XMLOutputFactory; 29 import javax.xml.stream.XMLStreamException; 30 import javax.xml.stream.XMLStreamWriter; 31 import javax.xml.transform.OutputKeys; 32 import javax.xml.transform.Transformer; 33 import javax.xml.transform.TransformerException; 34 import javax.xml.transform.TransformerFactory; 35 import javax.xml.transform.stream.StreamResult; 36 import javax.xml.transform.stream.StreamSource; 37 38 /** 39 * A class that knows about the structure of the telephonylookup.xml file. 40 */ 41 final class TelephonyLookupXmlFile { 42 43 // <telephony_lookup> 44 private static final String TELEPHONY_LOOKUP_ELEMENT = "telephony_lookup"; 45 46 // <networks> 47 private static final String NETWORKS_ELEMENT = "networks"; 48 49 // <network mcc="123" mnc="456" country="gu"> 50 private static final String NETWORK_ELEMENT = "network"; 51 private static final String MOBILE_COUNTRY_CODE_ATTRIBUTE = "mcc"; 52 private static final String MOBILE_NETWORK_CODE_ATTRIBUTE = "mnc"; 53 private static final String COUNTRY_ISO_CODE_ATTRIBUTE = "country"; 54 55 // <mobile_countries> 56 private static final String MOBILE_COUNTRIES_ELEMENT = "mobile_countries"; 57 58 // <mobile_country mcc="123" [default="gu"]> 59 private static final String MOBILE_COUNTRY_ELEMENT = "mobile_country"; 60 private static final String DEFAULT_ATTRIBUTE = "default"; 61 write(TelephonyLookup telephonyLookup, String outputFile)62 static void write(TelephonyLookup telephonyLookup, String outputFile) 63 throws XMLStreamException, IOException { 64 /* 65 * The required XML structure is: 66 * <telephony_lookup> 67 * <networks> 68 * <network mcc="123" mnc="456" country="zz"/> 69 * <network mcc="123" mnc="789" country="zz"/> 70 * </networks> 71 * 72 * <mobile_countries> 73 * <mobile_country mcc="310"/> 74 * <country>us</country> 75 * </mobile_country> 76 * <mobile_country mcc="340" default="gp"> 77 * <country>gp</country> 78 * <country>gf</country> 79 * </mobile_country> 80 * </mobile_countries> 81 * </telephony_lookup> 82 */ 83 84 StringWriter writer = new StringWriter(); 85 writeRaw(telephonyLookup, writer); 86 String rawXml = writer.getBuffer().toString(); 87 88 TransformerFactory factory = TransformerFactory.newInstance(); 89 try (Writer fileWriter = new OutputStreamWriter( 90 new FileOutputStream(outputFile), StandardCharsets.UTF_8)) { 91 92 // Transform the XML with the identity transform but with indenting 93 // so it's more human-readable. 94 Transformer transformer = factory.newTransformer(); 95 transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 96 transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "1"); 97 transformer.transform( 98 new StreamSource(new StringReader(rawXml)), new StreamResult(fileWriter)); 99 } catch (TransformerException e) { 100 throw new XMLStreamException(e); 101 } 102 } 103 writeRaw(TelephonyLookup telephonyLookup, Writer fileWriter)104 private static void writeRaw(TelephonyLookup telephonyLookup, Writer fileWriter) 105 throws XMLStreamException { 106 XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newFactory(); 107 XMLStreamWriter xmlWriter = xmlOutputFactory.createXMLStreamWriter(fileWriter); 108 xmlWriter.writeStartDocument(); 109 xmlWriter.writeComment("\n\n **** Autogenerated file - DO NOT EDIT ****\n\n"); 110 TelephonyLookup.writeXml(telephonyLookup, xmlWriter); 111 xmlWriter.writeEndDocument(); 112 } 113 114 static class TelephonyLookup { 115 private final List<Network> networks; 116 private final List<MobileCountry> mobileCountries; 117 TelephonyLookup(List<Network> networks, List<MobileCountry> mobileCountries)118 TelephonyLookup(List<Network> networks, List<MobileCountry> mobileCountries) { 119 this.networks = networks; 120 this.mobileCountries = mobileCountries; 121 } 122 writeXml(TelephonyLookup telephonyLookup, XMLStreamWriter writer)123 static void writeXml(TelephonyLookup telephonyLookup, XMLStreamWriter writer) 124 throws XMLStreamException { 125 writer.writeStartElement(TELEPHONY_LOOKUP_ELEMENT); 126 127 // Networks 128 writer.writeStartElement(NETWORKS_ELEMENT); 129 for (Network network : telephonyLookup.networks) { 130 network.writeXml(network, writer); 131 } 132 writer.writeEndElement(); // NETWORKS_ELEMENT 133 134 // Mobile Countries 135 writer.writeStartElement(MOBILE_COUNTRIES_ELEMENT); 136 for (MobileCountry mobileCountry : telephonyLookup.mobileCountries) { 137 mobileCountry.writeXml(mobileCountry, writer); 138 } 139 writer.writeEndElement(); // MOBILE_COUNTRIES_ELEMENT 140 141 writer.writeEndElement(); // TELEPHONY_LOOKUP_ELEMENT 142 } 143 } 144 145 static class Network { 146 147 private final String mcc; 148 private final String mnc; 149 private final String countryIsoCode; 150 Network(String mcc, String mnc, String countryIsoCode)151 Network(String mcc, String mnc, String countryIsoCode) { 152 this.mcc = Objects.requireNonNull(mcc); 153 this.mnc = Objects.requireNonNull(mnc); 154 this.countryIsoCode = Objects.requireNonNull(countryIsoCode); 155 } 156 writeXml(Network network, XMLStreamWriter writer)157 static void writeXml(Network network, XMLStreamWriter writer) 158 throws XMLStreamException { 159 writer.writeStartElement(NETWORK_ELEMENT); 160 writer.writeAttribute(MOBILE_COUNTRY_CODE_ATTRIBUTE, network.mcc); 161 writer.writeAttribute(MOBILE_NETWORK_CODE_ATTRIBUTE, network.mnc); 162 writer.writeAttribute(COUNTRY_ISO_CODE_ATTRIBUTE, network.countryIsoCode); 163 writer.writeEndElement(); // NETWORK_ELEMENT 164 } 165 } 166 167 static class MobileCountry { 168 169 private final String mcc; 170 private final List<String> countryIsoCodes; 171 MobileCountry(String mcc, List<String> countryIsoCodes)172 MobileCountry(String mcc, List<String> countryIsoCodes) { 173 this.mcc = Objects.requireNonNull(mcc); 174 this.countryIsoCodes = Objects.requireNonNull(countryIsoCodes); 175 } 176 writeXml(MobileCountry mobileCountry, XMLStreamWriter writer)177 static void writeXml(MobileCountry mobileCountry, XMLStreamWriter writer) 178 throws XMLStreamException { 179 writer.writeStartElement(MOBILE_COUNTRY_ELEMENT); 180 writer.writeAttribute(MOBILE_COUNTRY_CODE_ATTRIBUTE, mobileCountry.mcc); 181 182 if (mobileCountry.countryIsoCodes.size() > 1) { 183 writer.writeAttribute(DEFAULT_ATTRIBUTE, mobileCountry.countryIsoCodes.getFirst()); 184 } 185 186 for (String countryIsoCode : mobileCountry.countryIsoCodes) { 187 writer.writeStartElement(COUNTRY_ISO_CODE_ATTRIBUTE); 188 writer.writeCharacters(countryIsoCode); 189 writer.writeEndElement(); // COUNTRY_ISO_CODE_ATTRIBUTE 190 } 191 192 writer.writeEndElement(); // MOBILE_COUNTRY_ELEMENT 193 } 194 } 195 } 196