• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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