1 /* 2 * Copyright (C) 2012 The Libphonenumber Authors 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 17 package com.google.i18n.phonenumbers; 18 19 import java.io.IOException; 20 import java.io.PrintWriter; 21 import java.io.Writer; 22 import java.util.Locale; 23 24 /** 25 * Encapsulation of binary metadata created from XML to be included as static data in C++ source 26 * files. 27 * 28 * @author David Beaumont 29 * @author Philippe Liard 30 */ 31 public final class CppMetadataGenerator { 32 33 /** 34 * The metadata type represents the known types of metadata and includes additional information 35 * such as the copyright year. It is expected that the generated files will be named after the 36 * {@link #toString} of their type. 37 */ 38 public enum Type { 39 /** The basic phone number metadata (expected to be written to metadata.[h/cc]). */ 40 METADATA("metadata", 2011), 41 /** The alternate format metadata (expected to be written to alternate_format.[h/cc]). */ 42 ALTERNATE_FORMAT("alternate_format", 2012), 43 /** Metadata for short numbers (expected to be written to short_metadata.[h/cc]). */ 44 SHORT_NUMBERS("short_metadata", 2013); 45 46 private final String typeName; 47 private final int copyrightYear; 48 Type(String typeName, int copyrightYear)49 private Type(String typeName, int copyrightYear) { 50 this.typeName = typeName; 51 this.copyrightYear = copyrightYear; 52 } 53 54 /** Returns the year in which this metadata type was first introduced. */ getCopyrightYear()55 public int getCopyrightYear() { 56 return copyrightYear; 57 } 58 59 /** 60 * Returns the name of this type for use in C++ source/header files. Use this in preference to 61 * using {@link #name}. 62 */ toString()63 @Override public String toString() { 64 return typeName; 65 } 66 67 /** 68 * Parses the type from a string case-insensitively. 69 * 70 * @return the matching Type instance or null if not matched. 71 */ parse(String typeName)72 public static Type parse(String typeName) { 73 if (Type.METADATA.toString().equalsIgnoreCase(typeName)) { 74 return Type.METADATA; 75 } else if (Type.ALTERNATE_FORMAT.toString().equalsIgnoreCase(typeName)) { 76 return Type.ALTERNATE_FORMAT; 77 } else if (Type.SHORT_NUMBERS.toString().equalsIgnoreCase(typeName)) { 78 return Type.SHORT_NUMBERS; 79 } else { 80 return null; 81 } 82 } 83 } 84 85 /** 86 * Creates a metadata instance that can write C++ source and header files to represent this given 87 * byte array as a static unsigned char array. Note that a direct reference to the byte[] is 88 * retained by the newly created CppXmlMetadata instance, so the caller should treat the array as 89 * immutable after making this call. 90 */ create(Type type, byte[] data)91 public static CppMetadataGenerator create(Type type, byte[] data) { 92 return new CppMetadataGenerator(type, data); 93 } 94 95 private final Type type; 96 private final byte[] data; 97 private final String guardName; // e.g. "I18N_PHONENUMBERS_<TYPE>_H_" 98 private final String headerInclude; // e.g. "phonenumbers/<type>.h" 99 CppMetadataGenerator(Type type, byte[] data)100 private CppMetadataGenerator(Type type, byte[] data) { 101 this.type = type; 102 this.data = data; 103 this.guardName = createGuardName(type); 104 this.headerInclude = createHeaderInclude(type); 105 } 106 107 /** 108 * Writes the header file for the C++ representation of the metadata to the given writer. Note 109 * that this method does not close the given writer. 110 */ outputHeaderFile(Writer out)111 public void outputHeaderFile(Writer out) throws IOException { 112 PrintWriter pw = new PrintWriter(out); 113 CopyrightNotice.writeTo(pw, type.getCopyrightYear()); 114 pw.println("#ifndef " + guardName); 115 pw.println("#define " + guardName); 116 pw.println(); 117 emitNamespaceStart(pw); 118 pw.println(); 119 pw.println("int " + type + "_size();"); 120 pw.println("const void* " + type + "_get();"); 121 pw.println(); 122 emitNamespaceEnd(pw); 123 pw.println(); 124 pw.println("#endif // " + guardName); 125 pw.flush(); 126 } 127 128 /** 129 * Writes the source file for the C++ representation of the metadata, including a static array 130 * containing the data itself, to the given writer. Note that this method does not close the given 131 * writer. 132 */ outputSourceFile(Writer out)133 public void outputSourceFile(Writer out) throws IOException { 134 // TODO: Consider outputting a load method to return the parsed proto directly. 135 PrintWriter pw = new PrintWriter(out); 136 CopyrightNotice.writeTo(pw, type.getCopyrightYear()); 137 pw.println("#include \"" + headerInclude + "\""); 138 pw.println(); 139 emitNamespaceStart(pw); 140 pw.println(); 141 pw.println("namespace {"); 142 pw.println("static const unsigned char data[] = {"); 143 emitStaticArrayData(pw, data); 144 pw.println("};"); 145 pw.println("} // namespace"); 146 pw.println(); 147 pw.println("int " + type + "_size() {"); 148 pw.println(" return sizeof(data) / sizeof(data[0]);"); 149 pw.println("}"); 150 pw.println(); 151 pw.println("const void* " + type + "_get() {"); 152 pw.println(" return data;"); 153 pw.println("}"); 154 pw.println(); 155 emitNamespaceEnd(pw); 156 pw.flush(); 157 } 158 createGuardName(Type type)159 private static String createGuardName(Type type) { 160 return String.format("I18N_PHONENUMBERS_%s_H_", type.toString().toUpperCase(Locale.ENGLISH)); 161 } 162 createHeaderInclude(Type type)163 private static String createHeaderInclude(Type type) { 164 return String.format("phonenumbers/%s.h", type); 165 } 166 emitNamespaceStart(PrintWriter pw)167 private static void emitNamespaceStart(PrintWriter pw) { 168 pw.println("namespace i18n {"); 169 pw.println("namespace phonenumbers {"); 170 } 171 emitNamespaceEnd(PrintWriter pw)172 private static void emitNamespaceEnd(PrintWriter pw) { 173 pw.println("} // namespace phonenumbers"); 174 pw.println("} // namespace i18n"); 175 } 176 177 /** Emits the C++ code corresponding to the binary metadata as a static byte array. */ 178 // @VisibleForTesting emitStaticArrayData(PrintWriter pw, byte[] data)179 static void emitStaticArrayData(PrintWriter pw, byte[] data) { 180 String separator = " "; 181 for (int i = 0; i < data.length; i++) { 182 pw.print(separator); 183 emitHexByte(pw, data[i]); 184 separator = ((i + 1) % 13 == 0) ? ",\n " : ", "; 185 } 186 pw.println(); 187 } 188 189 /** Emits a single byte in the form 0xHH, where H is an upper case hex digit in [0-9A-F]. */ emitHexByte(PrintWriter pw, byte v)190 private static void emitHexByte(PrintWriter pw, byte v) { 191 pw.print("0x"); 192 pw.print(UPPER_HEX[(v & 0xF0) >>> 4]); 193 pw.print(UPPER_HEX[v & 0xF]); 194 } 195 196 private static final char[] UPPER_HEX = "0123456789ABCDEF".toCharArray(); 197 } 198