summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordbeaumont@google.com <dbeaumont@google.com@ee073f10-1060-11df-b6a4-87a95322a99c>2012-07-20 14:21:29 +0000
committerdbeaumont@google.com <dbeaumont@google.com@ee073f10-1060-11df-b6a4-87a95322a99c>2012-07-20 14:21:29 +0000
commit36a2c8aea21847e53f97dff6a4f545aa66cbe92c (patch)
treef9a577cb793c81e85ffddf37316dd4a69a7ce4d6
parentd89f3ed7baf1383842e666892e2066200ca06dca (diff)
downloadandroid_external_libphonenumbergoogle-36a2c8aea21847e53f97dff6a4f545aa66cbe92c.tar.gz
android_external_libphonenumbergoogle-36a2c8aea21847e53f97dff6a4f545aa66cbe92c.tar.bz2
android_external_libphonenumbergoogle-36a2c8aea21847e53f97dff6a4f545aa66cbe92c.zip
changes to allow for generation of alternate format metadata
git-svn-id: http://libphonenumber.googlecode.com/svn/trunk@507 ee073f10-1060-11df-b6a4-87a95322a99c
-rw-r--r--tools/java/common/src/com/google/i18n/phonenumbers/BuildMetadataFromXml.java59
-rw-r--r--tools/java/common/test/com/google/i18n/phonenumbers/BuildMetadataFromXmlTest.java53
-rw-r--r--tools/java/cpp-build/src/com/google/i18n/phonenumbers/BuildMetadataCppFromXml.java350
-rw-r--r--tools/java/cpp-build/src/com/google/i18n/phonenumbers/CppMetadataGenerator.java193
-rw-r--r--tools/java/cpp-build/target/cpp-build-1.0-SNAPSHOT-jar-with-dependencies.jarbin510163 -> 518506 bytes
-rw-r--r--tools/java/cpp-build/test/com/google/i18n/phonenumbers/BuildMetadataCppFromXmlTest.java164
-rw-r--r--tools/java/cpp-build/test/com/google/i18n/phonenumbers/CppMetadataGeneratorTest.java122
-rw-r--r--tools/java/java-build/target/java-build-1.0-SNAPSHOT-jar-with-dependencies.jarbin328384 -> 325242 bytes
8 files changed, 673 insertions, 268 deletions
diff --git a/tools/java/common/src/com/google/i18n/phonenumbers/BuildMetadataFromXml.java b/tools/java/common/src/com/google/i18n/phonenumbers/BuildMetadataFromXml.java
index a5fb1ca..67298ac 100644
--- a/tools/java/common/src/com/google/i18n/phonenumbers/BuildMetadataFromXml.java
+++ b/tools/java/common/src/com/google/i18n/phonenumbers/BuildMetadataFromXml.java
@@ -44,7 +44,6 @@ import javax.xml.parsers.DocumentBuilderFactory;
*/
public class BuildMetadataFromXml {
private static final Logger LOGGER = Logger.getLogger(BuildMetadataFromXml.class.getName());
- private static boolean liteBuild;
// String constants used to fetch the XML nodes and attributes.
private static final String CARRIER_CODE_FORMATTING_RULE = "carrierCodeFormattingRule";
@@ -82,15 +81,9 @@ public class BuildMetadataFromXml {
private static final String VOICEMAIL = "voicemail";
private static final String VOIP = "voip";
- // @VisibleForTesting
- static void setLiteBuild(boolean b) {
- liteBuild = b;
- }
-
// Build the PhoneMetadataCollection from the input XML file.
public static PhoneMetadataCollection buildPhoneMetadataCollection(String inputXmlFile,
boolean liteBuild) throws Exception {
- BuildMetadataFromXml.liteBuild = liteBuild;
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();
File xmlFile = new File(inputXmlFile);
@@ -108,7 +101,7 @@ public class BuildMetadataFromXml {
if (territoryElement.hasAttribute("id")) {
regionCode = territoryElement.getAttribute("id");
}
- PhoneMetadata metadata = loadCountryMetadata(regionCode, territoryElement);
+ PhoneMetadata metadata = loadCountryMetadata(regionCode, territoryElement, liteBuild);
metadataCollection.addMetadata(metadata);
}
return metadataCollection.build();
@@ -389,7 +382,8 @@ public class BuildMetadataFromXml {
// @VisibleForTesting
static PhoneNumberDesc.Builder processPhoneNumberDescElement(PhoneNumberDesc.Builder generalDesc,
Element countryElement,
- String numberType) {
+ String numberType,
+ boolean liteBuild) {
NodeList phoneNumberDescList = countryElement.getElementsByTagName(numberType);
PhoneNumberDesc.Builder numberDesc = PhoneNumberDesc.newBuilder();
if (phoneNumberDescList.getLength() == 0 && !isValidNumberType(numberType)) {
@@ -423,31 +417,42 @@ public class BuildMetadataFromXml {
}
// @VisibleForTesting
- static void loadGeneralDesc(PhoneMetadata.Builder metadata, Element element) {
+ static void loadGeneralDesc(PhoneMetadata.Builder metadata, Element element, boolean liteBuild) {
PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
- generalDesc = processPhoneNumberDescElement(generalDesc, element, GENERAL_DESC);
+ generalDesc = processPhoneNumberDescElement(generalDesc, element, GENERAL_DESC, liteBuild);
metadata.setGeneralDesc(generalDesc);
- metadata.setFixedLine(processPhoneNumberDescElement(generalDesc, element, FIXED_LINE));
- metadata.setMobile(processPhoneNumberDescElement(generalDesc, element, MOBILE));
- metadata.setTollFree(processPhoneNumberDescElement(generalDesc, element, TOLL_FREE));
- metadata.setPremiumRate(processPhoneNumberDescElement(generalDesc, element, PREMIUM_RATE));
- metadata.setSharedCost(processPhoneNumberDescElement(generalDesc, element, SHARED_COST));
- metadata.setVoip(processPhoneNumberDescElement(generalDesc, element, VOIP));
- metadata.setPersonalNumber(processPhoneNumberDescElement(generalDesc, element,
- PERSONAL_NUMBER));
- metadata.setPager(processPhoneNumberDescElement(generalDesc, element, PAGER));
- metadata.setUan(processPhoneNumberDescElement(generalDesc, element, UAN));
- metadata.setVoicemail(processPhoneNumberDescElement(generalDesc, element, VOICEMAIL));
- metadata.setEmergency(processPhoneNumberDescElement(generalDesc, element, EMERGENCY));
- metadata.setNoInternationalDialling(processPhoneNumberDescElement(generalDesc, element,
- NO_INTERNATIONAL_DIALLING));
+ metadata.setFixedLine(
+ processPhoneNumberDescElement(generalDesc, element, FIXED_LINE, liteBuild));
+ metadata.setMobile(
+ processPhoneNumberDescElement(generalDesc, element, MOBILE, liteBuild));
+ metadata.setTollFree(
+ processPhoneNumberDescElement(generalDesc, element, TOLL_FREE, liteBuild));
+ metadata.setPremiumRate(
+ processPhoneNumberDescElement(generalDesc, element, PREMIUM_RATE, liteBuild));
+ metadata.setSharedCost(
+ processPhoneNumberDescElement(generalDesc, element, SHARED_COST, liteBuild));
+ metadata.setVoip(
+ processPhoneNumberDescElement(generalDesc, element, VOIP, liteBuild));
+ metadata.setPersonalNumber(
+ processPhoneNumberDescElement(generalDesc, element, PERSONAL_NUMBER, liteBuild));
+ metadata.setPager(
+ processPhoneNumberDescElement(generalDesc, element, PAGER, liteBuild));
+ metadata.setUan(
+ processPhoneNumberDescElement(generalDesc, element, UAN, liteBuild));
+ metadata.setVoicemail(
+ processPhoneNumberDescElement(generalDesc, element, VOICEMAIL, liteBuild));
+ metadata.setEmergency(
+ processPhoneNumberDescElement(generalDesc, element, EMERGENCY, liteBuild));
+ metadata.setNoInternationalDialling(
+ processPhoneNumberDescElement(generalDesc, element, NO_INTERNATIONAL_DIALLING, liteBuild));
metadata.setSameMobileAndFixedLinePattern(
metadata.getMobile().getNationalNumberPattern().equals(
metadata.getFixedLine().getNationalNumberPattern()));
}
- public static PhoneMetadata loadCountryMetadata(String regionCode, Element element) {
+ // @VisibleForTesting
+ static PhoneMetadata loadCountryMetadata(String regionCode, Element element, boolean liteBuild) {
String nationalPrefix = getNationalPrefix(element);
PhoneMetadata.Builder metadata =
loadTerritoryTagMetadata(regionCode, element, nationalPrefix);
@@ -456,7 +461,7 @@ public class BuildMetadataFromXml {
loadAvailableFormats(metadata, element, nationalPrefix.toString(),
nationalPrefixFormattingRule.toString(),
element.hasAttribute(NATIONAL_PREFIX_OPTIONAL_WHEN_FORMATTING));
- loadGeneralDesc(metadata, element);
+ loadGeneralDesc(metadata, element, liteBuild);
return metadata.build();
}
}
diff --git a/tools/java/common/test/com/google/i18n/phonenumbers/BuildMetadataFromXmlTest.java b/tools/java/common/test/com/google/i18n/phonenumbers/BuildMetadataFromXmlTest.java
index f46da32..bed22b7 100644
--- a/tools/java/common/test/com/google/i18n/phonenumbers/BuildMetadataFromXmlTest.java
+++ b/tools/java/common/test/com/google/i18n/phonenumbers/BuildMetadataFromXmlTest.java
@@ -390,7 +390,7 @@ public class BuildMetadataFromXmlTest extends TestCase {
PhoneNumberDesc.Builder phoneNumberDesc;
phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
- generalDesc, territoryElement, "invalidType");
+ generalDesc, territoryElement, "invalidType", false);
assertEquals("NA", phoneNumberDesc.getPossibleNumberPattern());
assertEquals("NA", phoneNumberDesc.getNationalNumberPattern());
}
@@ -403,7 +403,7 @@ public class BuildMetadataFromXmlTest extends TestCase {
PhoneNumberDesc.Builder phoneNumberDesc;
phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
- generalDesc, territoryElement, "fixedLine");
+ generalDesc, territoryElement, "fixedLine", false);
assertEquals("\\d{6}", phoneNumberDesc.getPossibleNumberPattern());
}
@@ -419,30 +419,23 @@ public class BuildMetadataFromXmlTest extends TestCase {
PhoneNumberDesc.Builder phoneNumberDesc;
phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
- generalDesc, territoryElement, "fixedLine");
+ generalDesc, territoryElement, "fixedLine", false);
assertEquals("\\d{6}", phoneNumberDesc.getPossibleNumberPattern());
}
public void testProcessPhoneNumberDescElementHandlesLiteBuild()
throws ParserConfigurationException, SAXException, IOException {
- try {
- BuildMetadataFromXml.setLiteBuild(true);
- PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
- String xmlInput =
- "<territory><fixedLine>" +
- " <exampleNumber>01 01 01 01</exampleNumber>" +
- "</fixedLine></territory>";
- Element territoryElement = parseXmlString(xmlInput);
- PhoneNumberDesc.Builder phoneNumberDesc;
-
- phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
- generalDesc, territoryElement, "fixedLine");
- assertEquals("", phoneNumberDesc.getExampleNumber());
- } finally {
- // Restore the lite build parameter to its default value (false) to avoid potential
- // side-effects in other tests.
- BuildMetadataFromXml.setLiteBuild(false);
- }
+ PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
+ String xmlInput =
+ "<territory><fixedLine>" +
+ " <exampleNumber>01 01 01 01</exampleNumber>" +
+ "</fixedLine></territory>";
+ Element territoryElement = parseXmlString(xmlInput);
+ PhoneNumberDesc.Builder phoneNumberDesc;
+
+ phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
+ generalDesc, territoryElement, "fixedLine", true);
+ assertEquals("", phoneNumberDesc.getExampleNumber());
}
public void testProcessPhoneNumberDescOutputsExampleNumberByDefault()
@@ -450,13 +443,13 @@ public class BuildMetadataFromXmlTest extends TestCase {
PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
String xmlInput =
"<territory><fixedLine>" +
- " <exampleNumber>01 01 01 01</exampleNumber>" +
- "</fixedLine></territory>";
+ " <exampleNumber>01 01 01 01</exampleNumber>" +
+ "</fixedLine></territory>";
Element territoryElement = parseXmlString(xmlInput);
PhoneNumberDesc.Builder phoneNumberDesc;
phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
- generalDesc, territoryElement, "fixedLine");
+ generalDesc, territoryElement, "fixedLine", false);
assertEquals("01 01 01 01", phoneNumberDesc.getExampleNumber());
}
@@ -465,13 +458,13 @@ public class BuildMetadataFromXmlTest extends TestCase {
PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
String xmlInput =
"<territory><fixedLine>" +
- " <possibleNumberPattern>\t \\d { 6 } </possibleNumberPattern>" +
- "</fixedLine></territory>";
+ " <possibleNumberPattern>\t \\d { 6 } </possibleNumberPattern>" +
+ "</fixedLine></territory>";
Element countryElement = parseXmlString(xmlInput);
PhoneNumberDesc.Builder phoneNumberDesc;
phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
- generalDesc, countryElement, "fixedLine");
+ generalDesc, countryElement, "fixedLine", false);
assertEquals("\\d{6}", phoneNumberDesc.getPossibleNumberPattern());
}
@@ -486,7 +479,7 @@ public class BuildMetadataFromXmlTest extends TestCase {
Element territoryElement = parseXmlString(xmlInput);
PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
// Should set sameMobileAndFixedPattern to true.
- BuildMetadataFromXml.loadGeneralDesc(metadata, territoryElement);
+ BuildMetadataFromXml.loadGeneralDesc(metadata, territoryElement, false);
assertTrue(metadata.isSameMobileAndFixedLinePattern());
}
@@ -504,10 +497,10 @@ public class BuildMetadataFromXmlTest extends TestCase {
" <voip><nationalNumberPattern>\\d{8}</nationalNumberPattern></voip>" +
" <uan><nationalNumberPattern>\\d{9}</nationalNumberPattern></uan>" +
" <shortCode><nationalNumberPattern>\\d{10}</nationalNumberPattern></shortCode>" +
- "</territory>";
+ "</territory>";
Element territoryElement = parseXmlString(xmlInput);
PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
- BuildMetadataFromXml.loadGeneralDesc(metadata, territoryElement);
+ BuildMetadataFromXml.loadGeneralDesc(metadata, territoryElement, false);
assertEquals("\\d{1}", metadata.getFixedLine().getNationalNumberPattern());
assertEquals("\\d{2}", metadata.getMobile().getNationalNumberPattern());
assertEquals("\\d{3}", metadata.getPager().getNationalNumberPattern());
diff --git a/tools/java/cpp-build/src/com/google/i18n/phonenumbers/BuildMetadataCppFromXml.java b/tools/java/cpp-build/src/com/google/i18n/phonenumbers/BuildMetadataCppFromXml.java
index ddc01e3..baf3422 100644
--- a/tools/java/cpp-build/src/com/google/i18n/phonenumbers/BuildMetadataCppFromXml.java
+++ b/tools/java/cpp-build/src/com/google/i18n/phonenumbers/BuildMetadataCppFromXml.java
@@ -16,15 +16,19 @@
package com.google.i18n.phonenumbers;
-import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection;
+import com.google.i18n.phonenumbers.CppMetadataGenerator.Type;
import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.PrintWriter;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.nio.charset.Charset;
import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* This class generates the C++ code representation of the provided XML metadata file. It lets us
@@ -32,35 +36,112 @@ import java.util.Set;
* the code emitted by this class with the C++ phonenumber library.
*
* @author Philippe Liard
+ * @author David Beaumont
*/
public class BuildMetadataCppFromXml extends Command {
- // File path where the XML input can be found.
- private String inputFilePath;
- // Output directory where the generated files will be saved.
- private String outputDir;
- // 'metadata', 'test_metadata' or 'lite_metadata' depending on the value of the last command line
- // parameter.
- private String baseFilename;
- // Whether to generate "lite" metadata or not.
- private boolean liteMetadata;
- // The binary translation of the XML file is directly written to a byte array output stream
- // instead of creating an unnecessary file on the filesystem.
- private ByteArrayOutputStream binaryStream = new ByteArrayOutputStream();
-
- // Header (.h) file and implementation (.cc) file output streams.
- private FileOutputStream headerFileOutputStream;
- private FileOutputStream implFileOutputStream;
-
- private static final Set<String> METADATA_TYPES =
- new HashSet<String>(Arrays.asList("metadata", "test_metadata", "lite_metadata"));
-
- private static final int COPYRIGHT_YEAR = 2011;
+
+ /** An enum encapsulating the variations of metadata that we can produce. */
+ public enum Variant {
+ /** The default 'full' variant which contains all the metadata. */
+ FULL("%s"),
+ /** The test variant which contains fake data for tests. */
+ TEST("test_%s"),
+ /**
+ * The lite variant contains the same metadata as the full version but excludes any example
+ * data. This is typically used for clients with space restrictions.
+ */
+ LITE("lite_%s");
+
+ private final String template;
+
+ private Variant(String template) {
+ this.template = template;
+ }
+
+ /**
+ * Returns the basename of the type by adding the name of the current variant. The basename of
+ * a Type is used to determine the name of the source file in which the metadata is defined.
+ *
+ * <p>Note that when the variant is {@link Variant#FULL} this method just returns the type name.
+ */
+ public String getBasename(Type type) {
+ return String.format(template, type);
+ }
+
+ /**
+ * Parses metadata variant name. By default (for a name of {@code ""} or {@code null}) we return
+ * {@link Variant#FULL}, otherwise we match against the variant name (either "test" or "lite").
+ */
+ public static Variant parse(String variantName) {
+ if ("test".equalsIgnoreCase(variantName)) {
+ return Variant.TEST;
+ } else if ("lite".equalsIgnoreCase(variantName)) {
+ return Variant.LITE;
+ } else if (variantName == null || variantName.length() == 0) {
+ return Variant.FULL;
+ } else {
+ return null;
+ }
+ }
+ }
/**
- * Package private setter used to inject the binary stream for testing purpose.
+ * An immutable options class for parsing and representing the command line options for this
+ * command.
*/
- void setBinaryStream(ByteArrayOutputStream stream) {
- this.binaryStream = stream;
+ // @VisibleForTesting
+ static final class Options {
+ private static final Pattern BASENAME_PATTERN =
+ Pattern.compile("(?:(test|lite)_)?([a-z_]+)");
+
+ public static Options parse(String commandName, String[] args) {
+ if (args.length == 4) {
+ String inputXmlFilePath = args[1];
+ String outputDirPath = args[2];
+ Matcher basenameMatcher = BASENAME_PATTERN.matcher(args[3]);
+ if (basenameMatcher.matches()) {
+ Variant variant = Variant.parse(basenameMatcher.group(1));
+ Type type = Type.parse(basenameMatcher.group(2));
+ if (type != null && variant != null) {
+ return new Options(inputXmlFilePath, outputDirPath, type, variant);
+ }
+ }
+ }
+ throw new IllegalArgumentException(String.format(
+ "Usage: %s <inputXmlFile> <outputDir> ( <type> | test_<type> | lite_<type> )\n" +
+ " where <type> is one of: %s",
+ commandName, Arrays.asList(Type.values())));
+ }
+
+ // File path where the XML input can be found.
+ private final String inputXmlFilePath;
+ // Output directory where the generated files will be saved.
+ private final String outputDirPath;
+ private final Type type;
+ private final Variant variant;
+
+ private Options(String inputXmlFilePath, String outputDirPath, Type type, Variant variant) {
+ this.inputXmlFilePath = inputXmlFilePath;
+ this.outputDirPath = outputDirPath;
+ this.type = type;
+ this.variant = variant;
+ }
+
+ public String getInputFilePath() {
+ return inputXmlFilePath;
+ }
+
+ public String getOutputDir() {
+ return outputDirPath;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public Variant getVariant() {
+ return variant;
+ }
}
@Override
@@ -69,187 +150,70 @@ public class BuildMetadataCppFromXml extends Command {
}
/**
- * Starts the generation of the code. First it checks parameters from command line. Then it opens
- * all the streams (input and output streams), emits the header and implementation code and
- * finally closes all the streams.
+ * Generates C++ header and source files to represent the metadata specified by this command's
+ * arguments. The metadata XML file is read and converted to a byte array before being written
+ * into a C++ source file as a static data array.
*
* @return true if the generation succeeded.
*/
@Override
public boolean start() {
- if (!parseCommandLine()) {
- return false;
- }
try {
- generateBinaryFromXml();
- openFiles();
- emitHeader();
- emitImplementation();
- } catch (Exception e) {
+ Options opt = Options.parse(getCommandName(), getArgs());
+ byte[] data = loadMetadataBytes(opt.getInputFilePath(), opt.getVariant() == Variant.LITE);
+ CppMetadataGenerator metadata = CppMetadataGenerator.create(opt.getType(), data);
+
+ // TODO: Consider adding checking for correctness of file paths and access.
+ OutputStream headerStream = null;
+ OutputStream sourceStream = null;
+ try {
+ File dir = new File(opt.getOutputDir());
+ headerStream = openHeaderStream(dir, opt.getType());
+ sourceStream = openSourceStream(dir, opt.getType(), opt.getVariant());
+ metadata.outputHeaderFile(new OutputStreamWriter(headerStream, UTF_8));
+ metadata.outputSourceFile(new OutputStreamWriter(sourceStream, UTF_8));
+ } finally {
+ FileUtils.closeFiles(headerStream, sourceStream);
+ }
+ return true;
+ } catch (IOException e) {
+ System.err.println(e.getMessage());
+ } catch (RuntimeException e) {
System.err.println(e.getMessage());
- return false;
- } finally {
- FileUtils.closeFiles(headerFileOutputStream, implFileOutputStream);
}
- return true;
- }
-
- private void generateBinaryFromXml() throws Exception {
- PhoneMetadataCollection collection =
- BuildMetadataFromXml.buildPhoneMetadataCollection(inputFilePath, liteMetadata);
- collection.writeTo(binaryStream);
- }
-
- /**
- * Opens the binary file input stream and the two file output streams used to emit header and
- * implementation code.
- */
- private void openFiles() throws IOException {
- headerFileOutputStream = new FileOutputStream(String.format("%s/metadata.h", outputDir));
- implFileOutputStream = new FileOutputStream(String.format("%s/%s.cc", outputDir, baseFilename));
+ return false;
}
- private void emitNamespacesBeginning(PrintWriter pw) {
- pw.println("namespace i18n {");
- pw.println("namespace phonenumbers {");
+ /** Loads the metadata XML file and converts its contents to a byte array. */
+ private byte[] loadMetadataBytes(String inputFilePath, boolean liteMetadata) {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try {
+ writePhoneMetadataCollection(inputFilePath, liteMetadata, out);
+ } catch (Exception e) {
+ // We cannot recover from any exceptions thrown here, so promote them to runtime exceptions.
+ throw new RuntimeException(e);
+ } finally {
+ FileUtils.closeFiles(out);
+ }
+ return out.toByteArray();
}
- private void emitNamespacesEnd(PrintWriter pw) {
- pw.println("} // namespace phonenumbers");
- pw.println("} // namespace i18n");
+ // @VisibleForTesting
+ void writePhoneMetadataCollection(
+ String inputFilePath, boolean liteMetadata, OutputStream out) throws IOException, Exception {
+ BuildMetadataFromXml.buildPhoneMetadataCollection(inputFilePath, liteMetadata).writeTo(out);
}
- /**
- * Generates the header file containing the two function prototypes in namespace
- * i18n::phonenumbers.
- * <pre>
- * int metadata_size();
- * const void* metadata_get();
- * </pre>
- */
- private void emitHeader() throws IOException {
- final PrintWriter pw = new PrintWriter(headerFileOutputStream);
- CopyrightNotice.writeTo(pw, COPYRIGHT_YEAR);
- final String guardName = "I18N_PHONENUMBERS_METADATA_H_";
- pw.println("#ifndef " + guardName);
- pw.println("#define " + guardName);
-
- pw.println();
- emitNamespacesBeginning(pw);
- pw.println();
-
- pw.println("int metadata_size();");
- pw.println("const void* metadata_get();");
- pw.println();
-
- emitNamespacesEnd(pw);
- pw.println();
-
- pw.println("#endif // " + guardName);
- pw.close();
+ // @VisibleForTesting
+ OutputStream openHeaderStream(File dir, Type type) throws FileNotFoundException {
+ return new FileOutputStream(new File(dir, type + ".h"));
}
- /**
- * The next two methods generate the implementation file (.cc) containing the file data and the
- * two function implementations:
- *
- * <pre>
- * #include "X.h"
- *
- * namespace i18n {
- * namespace phonenumbers {
- *
- * namespace {
- * const unsigned char[] data = { .... };
- * } // namespace
- *
- * const void* metadata_get() {
- * return data;
- * }
- *
- * int metadata_size() {
- * return sizeof(data) / sizeof(data[0]);
- * }
- *
- * } // namespace phonenumbers
- * } // namespace i18n
- *
- * </pre>
- */
-
- /**
- * Emits the C++ code implementation (.cc file) corresponding to the provided XML input file.
- */
- private void emitImplementation() throws IOException {
- final PrintWriter pw = new PrintWriter(implFileOutputStream);
- CopyrightNotice.writeTo(pw, COPYRIGHT_YEAR);
- pw.println("#include \"phonenumbers/metadata.h\"");
- pw.println();
-
- emitNamespacesBeginning(pw);
- pw.println();
-
- pw.println("namespace {");
- pw.print("static const unsigned char data[] = {");
- emitStaticArrayCode(pw);
- pw.println("};");
- pw.println("} // namespace");
-
- pw.println();
- pw.println("int metadata_size() {");
- pw.println(" return sizeof(data) / sizeof(data[0]);");
- pw.println("}");
-
- pw.println();
- pw.println("const void* metadata_get() {");
- pw.println(" return data;");
- pw.println("}");
-
- pw.println();
- emitNamespacesEnd(pw);
-
- pw.close();
+ // @VisibleForTesting
+ OutputStream openSourceStream(File dir, Type type, Variant variant) throws FileNotFoundException {
+ return new FileOutputStream(new File(dir, variant.getBasename(type) + ".cc"));
}
- /**
- * Emits the C++ code corresponding to the provided XML input file into a static byte array.
- */
- void emitStaticArrayCode(PrintWriter pw) throws IOException {
- byte[] buf = binaryStream.toByteArray();
- pw.print("\n ");
-
- for (int i = 0; i < buf.length; i++) {
- String format = "0x%02X";
-
- if (i == buf.length - 1) {
- format += "\n";
- } else if ((i + 1) % 13 == 0) { // 13 bytes per line to have lines of 79 characters.
- format += ",\n ";
- } else {
- format += ", ";
- }
- pw.printf(format, buf[i]);
- }
- pw.flush();
- binaryStream.flush();
- binaryStream.close();
- }
-
- private boolean parseCommandLine() {
- final String[] args = getArgs();
-
- if (args.length != 4 || !METADATA_TYPES.contains(args[3])) {
- System.err.println(String.format(
- "Usage: %s <inputXmlFile> <outputDir> ( metadata | test_metadata | lite_metadata )",
- getCommandName()));
- return false;
- }
- // args[0] is the name of the command.
- inputFilePath = args[1];
- outputDir = args[2];
- baseFilename = args[3];
- liteMetadata = baseFilename.equals("lite_metadata");
-
- return true;
- }
+ /** The charset in which our source and header files will be written. */
+ private static final Charset UTF_8 = Charset.forName("UTF-8");
}
diff --git a/tools/java/cpp-build/src/com/google/i18n/phonenumbers/CppMetadataGenerator.java b/tools/java/cpp-build/src/com/google/i18n/phonenumbers/CppMetadataGenerator.java
new file mode 100644
index 0000000..f1c6b34
--- /dev/null
+++ b/tools/java/cpp-build/src/com/google/i18n/phonenumbers/CppMetadataGenerator.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2012 The Libphonenumber Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.i18n.phonenumbers;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Locale;
+
+/**
+ * Encapsulation of binary metadata created from XML to be included as static data in C++ source
+ * files.
+ *
+ * @author David Beaumont
+ * @author Philippe Liard
+ */
+public final class CppMetadataGenerator {
+
+ /**
+ * The metadata type represents the known types of metadata and includes additional information
+ * such as the copyright year. It is expected that the generated files will be named after the
+ * {@link #toString} of their type.
+ */
+ public enum Type {
+ /** The basic phone number metadata (expected to be written to metadata.[h/cc]). */
+ METADATA("metadata", 2011),
+ /** The alternate format metadata (expected to be written to alternate_format.[h/cc]). */
+ ALTERNATE_FORMAT("alternate_format", 2012);
+
+ private final String typeName;
+ private final int copyrightYear;
+
+ private Type(String typeName, int copyrightYear) {
+ this.typeName = typeName;
+ this.copyrightYear = copyrightYear;
+ }
+
+ /** Returns the year in which this metadata type was first introduced. */
+ public int getCopyrightYear() {
+ return copyrightYear;
+ }
+
+ /**
+ * Returns the name of this type for use in C++ source/header files. Use this in preference to
+ * using {@link #name}.
+ */
+ @Override public String toString() {
+ return typeName;
+ }
+
+ /**
+ * Parses the type from a string case-insensitively.
+ *
+ * @return the matching Type instance or null if not matched.
+ */
+ public static Type parse(String typeName) {
+ if (Type.METADATA.toString().equalsIgnoreCase(typeName)) {
+ return Type.METADATA;
+ } else if (Type.ALTERNATE_FORMAT.toString().equalsIgnoreCase(typeName)) {
+ return Type.ALTERNATE_FORMAT;
+ } else {
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Creates a metadata instance that can write C++ source and header files to represent this given
+ * byte array as a static unsigned char array. Note that a direct reference to the byte[] is
+ * retained by the newly created CppXmlMetadata instance, so the caller should treat the array as
+ * immutable after making this call.
+ */
+ public static CppMetadataGenerator create(Type type, byte[] data) {
+ return new CppMetadataGenerator(type, data);
+ }
+
+ private final Type type;
+ private final byte[] data;
+ private final String guardName; // e.g. "I18N_PHONENUMBERS_<TYPE>_H_"
+ private final String headerInclude; // e.g. "phonenumbers/<type>.h"
+
+ private CppMetadataGenerator(Type type, byte[] data) {
+ this.type = type;
+ this.data = data;
+ this.guardName = createGuardName(type);
+ this.headerInclude = createHeaderInclude(type);
+ }
+
+ /**
+ * Writes the header file for the C++ representation of the metadata to the given writer. Note
+ * that this method does not close the given writer.
+ */
+ public void outputHeaderFile(Writer out) throws IOException {
+ PrintWriter pw = new PrintWriter(out);
+ CopyrightNotice.writeTo(pw, type.getCopyrightYear());
+ pw.println("#ifndef " + guardName);
+ pw.println("#define " + guardName);
+ pw.println();
+ emitNamespaceStart(pw);
+ pw.println();
+ pw.println("int " + type + "_size();");
+ pw.println("const void* " + type + "_get();");
+ pw.println();
+ emitNamespaceEnd(pw);
+ pw.println();
+ pw.println("#endif // " + guardName);
+ pw.flush();
+ }
+
+ /**
+ * Writes the source file for the C++ representation of the metadata, including a static array
+ * containing the data itself, to the given writer. Note that this method does not close the given
+ * writer.
+ */
+ public void outputSourceFile(Writer out) throws IOException {
+ // TODO: Consider outputting a load method to return the parsed proto directly.
+ PrintWriter pw = new PrintWriter(out);
+ CopyrightNotice.writeTo(pw, type.getCopyrightYear());
+ pw.println("#include \"" + headerInclude + "\"");
+ pw.println();
+ emitNamespaceStart(pw);
+ pw.println();
+ pw.println("namespace {");
+ pw.println("static const unsigned char data[] = {");
+ emitStaticArrayData(pw, data);
+ pw.println("};");
+ pw.println("} // namespace");
+ pw.println();
+ pw.println("int " + type + "_size() {");
+ pw.println(" return sizeof(data) / sizeof(data[0]);");
+ pw.println("}");
+ pw.println();
+ pw.println("const void* " + type + "_get() {");
+ pw.println(" return data;");
+ pw.println("}");
+ pw.println();
+ emitNamespaceEnd(pw);
+ pw.flush();
+ }
+
+ private static String createGuardName(Type type) {
+ return String.format("I18N_PHONENUMBERS_%s_H_", type.toString().toUpperCase(Locale.ENGLISH));
+ }
+
+ private static String createHeaderInclude(Type type) {
+ return String.format("phonenumbers/%s.h", type);
+ }
+
+ private static void emitNamespaceStart(PrintWriter pw) {
+ pw.println("namespace i18n {");
+ pw.println("namespace phonenumbers {");
+ }
+
+ private static void emitNamespaceEnd(PrintWriter pw) {
+ pw.println("} // namespace phonenumbers");
+ pw.println("} // namespace i18n");
+ }
+
+ /** Emits the C++ code corresponding to the binary metadata as a static byte array. */
+ // @VisibleForTesting
+ static void emitStaticArrayData(PrintWriter pw, byte[] data) {
+ String separator = " ";
+ for (int i = 0; i < data.length; i++) {
+ pw.print(separator);
+ emitHexByte(pw, data[i]);
+ separator = ((i + 1) % 13 == 0) ? ",\n " : ", ";
+ }
+ pw.println();
+ }
+
+ /** Emits a single byte in the form 0xHH, where H is an upper case hex digit in [0-9A-F]. */
+ private static void emitHexByte(PrintWriter pw, byte v) {
+ pw.print("0x");
+ pw.print(UPPER_HEX[(v & 0xF0) >>> 4]);
+ pw.print(UPPER_HEX[v & 0xF]);
+ }
+
+ private static final char[] UPPER_HEX = "0123456789ABCDEF".toCharArray();
+}
diff --git a/tools/java/cpp-build/target/cpp-build-1.0-SNAPSHOT-jar-with-dependencies.jar b/tools/java/cpp-build/target/cpp-build-1.0-SNAPSHOT-jar-with-dependencies.jar
index 2bdd1bd..a73e472 100644
--- a/tools/java/cpp-build/target/cpp-build-1.0-SNAPSHOT-jar-with-dependencies.jar
+++ b/tools/java/cpp-build/target/cpp-build-1.0-SNAPSHOT-jar-with-dependencies.jar
Binary files differ
diff --git a/tools/java/cpp-build/test/com/google/i18n/phonenumbers/BuildMetadataCppFromXmlTest.java b/tools/java/cpp-build/test/com/google/i18n/phonenumbers/BuildMetadataCppFromXmlTest.java
index e1c5eed..b1976e8 100644
--- a/tools/java/cpp-build/test/com/google/i18n/phonenumbers/BuildMetadataCppFromXmlTest.java
+++ b/tools/java/cpp-build/test/com/google/i18n/phonenumbers/BuildMetadataCppFromXmlTest.java
@@ -17,41 +17,169 @@
package com.google.i18n.phonenumbers;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import com.google.i18n.phonenumbers.BuildMetadataCppFromXml.Options;
+import com.google.i18n.phonenumbers.BuildMetadataCppFromXml.Variant;
+import com.google.i18n.phonenumbers.CppMetadataGenerator.Type;
+
import org.junit.Test;
import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
+import java.io.File;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
/**
- * Tests the BuildMetadataCppFromXml implementation to make sure it emits the expected code.
+ * Tests the BuildMetadataCppFromXml implementation to make sure it parses command line options and
+ * generates code correctly.
*/
public class BuildMetadataCppFromXmlTest {
+ // Various repeated test strings and data.
+ private static final String IGNORED = "IGNORED";
+ private static final String OUTPUT_DIR = "output/dir";
+ private static final String INPUT_PATH_XML = "input/path.xml";
+ private static final byte[] TEST_DATA =
+ new byte[] { (byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE };
+ private static final String CPP_TEST_DATA = "0xCA, 0xFE, 0xBA, 0xBE";
+
@Test
- public void emitStaticArrayCode() {
- final int streamSize = 4;
+ public void parseVariant() {
+ assertNull(Variant.parse("xxx"));
+ assertEquals(Variant.FULL, Variant.parse(null));
+ assertEquals(Variant.FULL, Variant.parse(""));
+ assertEquals(Variant.LITE, Variant.parse("lite"));
+ assertEquals(Variant.TEST, Variant.parse("test"));
+ assertEquals(Variant.LITE, Variant.parse("LITE"));
+ assertEquals(Variant.TEST, Variant.parse("Test"));
+ }
+ @Test
+ public void parseBadOptions() {
try {
- ByteArrayOutputStream stream = new ByteArrayOutputStream(streamSize);
+ BuildMetadataCppFromXml.Options.parse("MyCommand", new String[] { IGNORED });
+ fail("Expected exception not thrown");
+ } catch (IllegalArgumentException e) {
+ assertTrue(e.getMessage().contains("MyCommand"));
+ }
+ }
+
+ @Test
+ public void parseGoodOptions() {
+ Options opt = BuildMetadataCppFromXml.Options.parse("MyCommand",
+ new String[] { IGNORED, INPUT_PATH_XML, OUTPUT_DIR, "test_alternate_format" });
+ assertEquals(Type.ALTERNATE_FORMAT, opt.getType());
+ assertEquals(Variant.TEST, opt.getVariant());
+ assertEquals(INPUT_PATH_XML, opt.getInputFilePath());
+ assertEquals(OUTPUT_DIR, opt.getOutputDir());
+ }
+
+ @Test
+ public void generateMetadata() {
+ String[] args = new String[] {
+ IGNORED, INPUT_PATH_XML, OUTPUT_DIR, "metadata" };
+ // Most of the useful asserts are done in the mock class.
+ MockedCommand command = new MockedCommand(
+ INPUT_PATH_XML, false, OUTPUT_DIR, Type.METADATA, Variant.FULL);
+ command.setArgs(args);
+ command.start();
+ // Sanity check the captured data (asserting implicitly that the mocked methods were called).
+ String headerString = command.capturedHeaderFile();
+ assertTrue(headerString.contains("const void* metadata_get()"));
+ assertTrue(headerString.contains("int metadata_size()"));
+ String sourceString = command.capturedSourceFile();
+ assertTrue(sourceString.contains("const void* metadata_get()"));
+ assertTrue(sourceString.contains("int metadata_size()"));
+ assertTrue(sourceString.contains(CPP_TEST_DATA));
+ }
+
+ @Test
+ public void generateLiteMetadata() {
+ String[] args = new String[] {
+ IGNORED, INPUT_PATH_XML, OUTPUT_DIR, "lite_metadata" };
+ // Most of the useful asserts are done in the mock class.
+ MockedCommand command = new MockedCommand(
+ INPUT_PATH_XML, true, OUTPUT_DIR, Type.METADATA, Variant.LITE);
+ command.setArgs(args);
+ command.start();
+ // Sanity check the captured data (asserting implicitly that the mocked methods were called).
+ String headerString = command.capturedHeaderFile();
+ assertTrue(headerString.contains("const void* metadata_get()"));
+ assertTrue(headerString.contains("int metadata_size()"));
+ String sourceString = command.capturedSourceFile();
+ assertTrue(sourceString.contains("const void* metadata_get()"));
+ assertTrue(sourceString.contains("int metadata_size()"));
+ assertTrue(sourceString.contains(CPP_TEST_DATA));
+ }
- stream.write(0xca);
- stream.write(0xfe);
- stream.write(0xba);
- stream.write(0xbe);
+ @Test
+ public void generateAlternateFormat() {
+ String[] args = new String[] {
+ IGNORED, INPUT_PATH_XML, OUTPUT_DIR, "alternate_format" };
+ // Most of the useful asserts are done in the mock class.
+ MockedCommand command = new MockedCommand(
+ INPUT_PATH_XML, false, OUTPUT_DIR, Type.ALTERNATE_FORMAT, Variant.FULL);
+ command.setArgs(args);
+ command.start();
+ // Sanity check the captured data (asserting implicitly that the mocked methods were called).
+ String headerString = command.capturedHeaderFile();
+ assertTrue(headerString.contains("const void* alternate_format_get()"));
+ assertTrue(headerString.contains("int alternate_format_size()"));
+ String sourceString = command.capturedSourceFile();
+ assertTrue(sourceString.contains("const void* alternate_format_get()"));
+ assertTrue(sourceString.contains("int alternate_format_size()"));
+ assertTrue(sourceString.contains(CPP_TEST_DATA));
+ }
- ByteArrayOutputStream result = new ByteArrayOutputStream(streamSize);
- PrintWriter printWriter = new PrintWriter(result);
+ /**
+ * Manually mocked subclass of BuildMetadataCppFromXml which overrides all file related behavior
+ * while asserting the validity of any parameters passed to the mocked methods. After starting
+ * this command, the captured header and source file contents can be retrieved for testing.
+ */
+ static class MockedCommand extends BuildMetadataCppFromXml {
+ private static final Charset UTF_8 = Charset.forName("UTF-8");
+ private final String expectedInputFilePath;
+ private final boolean expectedLiteMetadata;
+ private final String expectedOutputDirPath;
+ private final Type expectedType;
+ private final Variant expectedVariant;
+ private final ByteArrayOutputStream headerOut = new ByteArrayOutputStream();
+ private final ByteArrayOutputStream sourceOut = new ByteArrayOutputStream();
- BuildMetadataCppFromXml buildMetadataCppFromXml = new BuildMetadataCppFromXml();
- buildMetadataCppFromXml.setBinaryStream(stream);
- buildMetadataCppFromXml.emitStaticArrayCode(printWriter);
+ public MockedCommand(String expectedInputFilePath, boolean expectedLiteMetadata,
+ String expectedOutputDirPath, Type expectedType, Variant expectedVariant) {
- assertEquals("\n 0xCA, 0xFE, 0xBA, 0xBE\n", result.toString());
- } catch (IOException e) {
- fail(e.getMessage());
+ this.expectedInputFilePath = expectedInputFilePath;
+ this.expectedLiteMetadata = expectedLiteMetadata;
+ this.expectedOutputDirPath = expectedOutputDirPath;
+ this.expectedType = expectedType;
+ this.expectedVariant = expectedVariant;
+ }
+ @Override void writePhoneMetadataCollection(
+ String inputFilePath, boolean liteMetadata, OutputStream out) throws Exception {
+ assertEquals(expectedInputFilePath, inputFilePath);
+ assertEquals(expectedLiteMetadata, liteMetadata);
+ out.write(TEST_DATA, 0, TEST_DATA.length);
+ }
+ @Override OutputStream openHeaderStream(File dir, Type type) {
+ assertEquals(expectedOutputDirPath, dir.getPath());
+ assertEquals(expectedType, type);
+ return headerOut;
+ }
+ @Override OutputStream openSourceStream(File dir, Type type, Variant variant) {
+ assertEquals(expectedOutputDirPath, dir.getPath());
+ assertEquals(expectedType, type);
+ assertEquals(expectedVariant, variant);
+ return sourceOut;
+ }
+ String capturedHeaderFile() {
+ return new String(headerOut.toByteArray(), UTF_8);
+ }
+ String capturedSourceFile() {
+ return new String(sourceOut.toByteArray(), UTF_8);
}
}
}
diff --git a/tools/java/cpp-build/test/com/google/i18n/phonenumbers/CppMetadataGeneratorTest.java b/tools/java/cpp-build/test/com/google/i18n/phonenumbers/CppMetadataGeneratorTest.java
new file mode 100644
index 0000000..4cd9bee
--- /dev/null
+++ b/tools/java/cpp-build/test/com/google/i18n/phonenumbers/CppMetadataGeneratorTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2012 The Libphonenumber Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.i18n.phonenumbers;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.google.i18n.phonenumbers.CppMetadataGenerator.Type;
+
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Tests that the CppXmlMetadata class emits the expected source and header files for metadata.
+ */
+public class CppMetadataGeneratorTest {
+
+ @Test
+ public void emitStaticArrayData() {
+ // 13 bytes per line, so have 16 bytes to test > 1 line (general case).
+ // Use all hex digits in both nibbles to test hex formatting.
+ byte[] data = new byte[] {
+ (byte) 0xF0, (byte) 0xE1, (byte) 0xD2, (byte) 0xC3,
+ (byte) 0xB4, (byte) 0xA5, (byte) 0x96, (byte) 0x87,
+ (byte) 0x78, (byte) 0x69, (byte) 0x5A, (byte) 0x4B,
+ (byte) 0x3C, (byte) 0x2D, (byte) 0x1E, (byte) 0x0F,
+ };
+
+ StringWriter writer = new StringWriter();
+ CppMetadataGenerator.emitStaticArrayData(new PrintWriter(writer), data);
+ assertEquals(
+ " 0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87, 0x78, 0x69, 0x5A, 0x4B, 0x3C,\n" +
+ " 0x2D, 0x1E, 0x0F\n",
+ writer.toString());
+ }
+
+ @Test
+ public void outputHeaderFile() throws IOException {
+ byte[] data = new byte[] { (byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE };
+ CppMetadataGenerator metadata = CppMetadataGenerator.create(Type.METADATA, data);
+
+ StringWriter writer = new StringWriter();
+ metadata.outputHeaderFile(writer);
+ Iterator<String> lines = toLines(writer.toString()).iterator();
+ // Sanity check that at least some of the expected lines are present.
+ assertTrue(consumeUntil(" * Copyright (C) 2011 The Libphonenumber Authors", lines));
+ assertTrue(consumeUntil("#ifndef I18N_PHONENUMBERS_METADATA_H_", lines));
+ assertTrue(consumeUntil("#define I18N_PHONENUMBERS_METADATA_H_", lines));
+ assertTrue(consumeUntil("namespace i18n {", lines));
+ assertTrue(consumeUntil("namespace phonenumbers {", lines));
+ assertTrue(consumeUntil("int metadata_size();", lines));
+ assertTrue(consumeUntil("const void* metadata_get();", lines));
+ assertTrue(consumeUntil("#endif // I18N_PHONENUMBERS_METADATA_H_", lines));
+ }
+
+ @Test
+ public void outputSourceFile() throws IOException {
+ byte[] data = new byte[] { (byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE };
+ CppMetadataGenerator metadata = CppMetadataGenerator.create(Type.ALTERNATE_FORMAT, data);
+
+ StringWriter writer = new StringWriter();
+ metadata.outputSourceFile(writer);
+ Iterator<String> lines = toLines(writer.toString()).iterator();
+ // Sanity check that at least some of the expected lines are present.
+ assertTrue(consumeUntil(" * Copyright (C) 2012 The Libphonenumber Authors", lines));
+ assertTrue(consumeUntil("namespace i18n {", lines));
+ assertTrue(consumeUntil("namespace phonenumbers {", lines));
+ assertTrue(consumeUntil("namespace {", lines));
+ assertTrue(consumeUntil("static const unsigned char data[] = {", lines));
+ assertTrue(consumeUntil(" 0xCA, 0xFE, 0xBA, 0xBE", lines));
+ assertTrue(consumeUntil("int alternate_format_size() {", lines));
+ assertTrue(consumeUntil("const void* alternate_format_get() {", lines));
+ }
+
+ /** Converts a string containing newlines into a list of lines. */
+ private static List<String> toLines(String s) throws IOException {
+ BufferedReader reader = new BufferedReader(new StringReader(s));
+ List<String> lines = new ArrayList<String>();
+ for (String line = reader.readLine(); line != null; line = reader.readLine()) {
+ lines.add(line);
+ }
+ return lines;
+ }
+
+ /**
+ * Consumes strings from the given iterator until the expected string is reached (it is also
+ * consumed). If the expected string is not found, the iterator is exhausted and {@code false} is
+ * returned.
+ *
+ * @return true if the expected string was found while consuming the iterator.
+ */
+ private static boolean consumeUntil(String expected, Iterator<String> it) {
+ while (it.hasNext()) {
+ if (it.next().equals(expected)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/tools/java/java-build/target/java-build-1.0-SNAPSHOT-jar-with-dependencies.jar b/tools/java/java-build/target/java-build-1.0-SNAPSHOT-jar-with-dependencies.jar
index 2d63a11..0416952 100644
--- a/tools/java/java-build/target/java-build-1.0-SNAPSHOT-jar-with-dependencies.jar
+++ b/tools/java/java-build/target/java-build-1.0-SNAPSHOT-jar-with-dependencies.jar
Binary files differ