Browse Source

TOOLS: Adding metadata code generation to C++ build tools.

pull/567/head
Philip Liard 15 years ago
committed by Mihaela Rosca
parent
commit
88fc7a704d
8 changed files with 671 additions and 41 deletions
  1. +0
    -37
      tools/java/common/pom.xml
  2. +329
    -0
      tools/java/common/src/com/google/i18n/phonenumbers/tools/BuildMetadataFromXml.java
  3. +44
    -0
      tools/java/common/src/com/google/i18n/phonenumbers/tools/CopyrightNotice.java
  4. +6
    -3
      tools/java/cpp-build/pom.xml
  5. +201
    -0
      tools/java/cpp-build/src/com/google/i18n/phonenumbers/tools/BuildMetadataCppFromXml.java
  6. +33
    -0
      tools/java/cpp-build/src/com/google/i18n/phonenumbers/tools/EntryPoint.java
  7. +57
    -0
      tools/java/cpp-build/test/com/google/i18n/phonenumbers/tools/BuildMetadataCppFromXmlTest.java
  8. +1
    -1
      tools/java/pom.xml

+ 0
- 37
tools/java/common/pom.xml View File

@ -1,37 +0,0 @@
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>tools</artifactId>
<groupId>com.google.i18n.phonenumbers</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.google.i18n.phonenumbers.tools</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
<name>Libphonenumber common library for build tools</name>
<description>
This library contains helper classes designed to ease file manipulation and command dispatching
which is required by build tools dealing with code generation and multiple commands invocation
from a single entry point.
</description>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<version>2.3.2</version>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

+ 329
- 0
tools/java/common/src/com/google/i18n/phonenumbers/tools/BuildMetadataFromXml.java View File

@ -0,0 +1,329 @@
/*
* Copyright (C) 2009 Google Inc.
*
* 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.tools;
import com.google.i18n.phonenumbers.Phonemetadata.NumberFormat;
import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata;
import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection;
import com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
/**
* Library to build phone number metadata from the XML format.
*
* @author Shaopeng Jia
*/
public class BuildMetadataFromXml {
private static final Logger LOGGER = Logger.getLogger(BuildMetadataFromXml.class.getName());
private static Boolean liteBuild;
// 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);
Document document = builder.parse(xmlFile);
document.getDocumentElement().normalize();
Element rootElement = document.getDocumentElement();
NodeList territory = rootElement.getElementsByTagName("territory");
PhoneMetadataCollection.Builder metadataCollection = PhoneMetadataCollection.newBuilder();
int numOfTerritories = territory.getLength();
for (int i = 0; i < numOfTerritories; i++) {
Element territoryElement = (Element) territory.item(i);
String regionCode = territoryElement.getAttribute("id");
PhoneMetadata metadata = loadCountryMetadata(regionCode, territoryElement);
metadataCollection.addMetadata(metadata);
}
return metadataCollection.build();
}
// Build a mapping from a country calling code to the region codes which denote the country/region
// represented by that country code. In the case of multiple countries sharing a calling code,
// such as the NANPA countries, the one indicated with "isMainCountryForCode" in the metadata
// should be first.
public static Map<Integer, List<String>> buildCountryCodeToRegionCodeMap(
PhoneMetadataCollection metadataCollection) {
Map<Integer, List<String>> countryCodeToRegionCodeMap =
new TreeMap<Integer, List<String>>();
for (PhoneMetadata metadata : metadataCollection.getMetadataList()) {
String regionCode = metadata.getId();
int countryCode = metadata.getCountryCode();
if (countryCodeToRegionCodeMap.containsKey(countryCode)) {
if (metadata.getMainCountryForCode()) {
countryCodeToRegionCodeMap.get(countryCode).add(0, regionCode);
} else {
countryCodeToRegionCodeMap.get(countryCode).add(regionCode);
}
} else {
// For most countries, there will be only one region code for the country calling code.
List<String> listWithRegionCode = new ArrayList<String>(1);
listWithRegionCode.add(regionCode);
countryCodeToRegionCodeMap.put(countryCode, listWithRegionCode);
}
}
return countryCodeToRegionCodeMap;
}
private static String validateRE(String regex) {
return validateRE(regex, false);
}
private static String validateRE(String regex, boolean removeWhitespace) {
// Removes all the whitespace and newline from the regexp. Not using pattern compile options to
// make it work across programming languages.
if (removeWhitespace) {
regex = regex.replaceAll("\\s", "");
}
Pattern.compile(regex);
// return regex itself if it is of correct regex syntax
// i.e. compile did not fail with a PatternSyntaxException.
return regex;
}
private static PhoneMetadata loadCountryMetadata(String regionCode, Element element) {
PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
metadata.setId(regionCode);
metadata.setCountryCode(Integer.parseInt(element.getAttribute("countryCode")));
if (element.hasAttribute("leadingDigits")) {
metadata.setLeadingDigits(validateRE(element.getAttribute("leadingDigits")));
}
metadata.setInternationalPrefix(validateRE(element.getAttribute("internationalPrefix")));
if (element.hasAttribute("preferredInternationalPrefix")) {
String preferredInternationalPrefix = element.getAttribute("preferredInternationalPrefix");
metadata.setPreferredInternationalPrefix(preferredInternationalPrefix);
}
if (element.hasAttribute("nationalPrefixForParsing")) {
metadata.setNationalPrefixForParsing(
validateRE(element.getAttribute("nationalPrefixForParsing")));
if (element.hasAttribute("nationalPrefixTransformRule")) {
metadata.setNationalPrefixTransformRule(
validateRE(element.getAttribute("nationalPrefixTransformRule")));
}
}
String nationalPrefix = "";
String nationalPrefixFormattingRule = "";
if (element.hasAttribute("nationalPrefix")) {
nationalPrefix = element.getAttribute("nationalPrefix");
metadata.setNationalPrefix(nationalPrefix);
nationalPrefixFormattingRule =
getNationalPrefixFormattingRuleFromElement(element, nationalPrefix);
if (!metadata.hasNationalPrefixForParsing()) {
metadata.setNationalPrefixForParsing(nationalPrefix);
}
}
String carrierCodeFormattingRule = "";
if (element.hasAttribute("carrierCodeFormattingRule")) {
carrierCodeFormattingRule = validateRE(
getDomesticCarrierCodeFormattingRuleFromElement(element, nationalPrefix));
}
if (element.hasAttribute("preferredExtnPrefix")) {
metadata.setPreferredExtnPrefix(element.getAttribute("preferredExtnPrefix"));
}
if (element.hasAttribute("mainCountryForCode")) {
metadata.setMainCountryForCode(true);
}
if (element.hasAttribute("leadingZeroPossible")) {
metadata.setLeadingZeroPossible(true);
}
// Extract availableFormats
NodeList numberFormatElements = element.getElementsByTagName("numberFormat");
int numOfFormatElements = numberFormatElements.getLength();
if (numOfFormatElements > 0) {
for (int i = 0; i < numOfFormatElements; i++) {
Element numberFormatElement = (Element) numberFormatElements.item(i);
NumberFormat.Builder format = NumberFormat.newBuilder();
if (numberFormatElement.hasAttribute("nationalPrefixFormattingRule")) {
format.setNationalPrefixFormattingRule(
getNationalPrefixFormattingRuleFromElement(numberFormatElement, nationalPrefix));
} else {
format.setNationalPrefixFormattingRule(nationalPrefixFormattingRule);
}
if (numberFormatElement.hasAttribute("carrierCodeFormattingRule")) {
format.setDomesticCarrierCodeFormattingRule(validateRE(
getDomesticCarrierCodeFormattingRuleFromElement(numberFormatElement,
nationalPrefix)));
} else {
format.setDomesticCarrierCodeFormattingRule(carrierCodeFormattingRule);
}
setLeadingDigitsPatterns(numberFormatElement, format);
format.setPattern(validateRE(numberFormatElement.getAttribute("pattern")));
NodeList formatPattern = numberFormatElement.getElementsByTagName("format");
if (formatPattern.getLength() != 1) {
LOGGER.log(Level.SEVERE,
"Only one format pattern for a numberFormat element should be defined.");
throw new RuntimeException("Invalid number of format patterns for country: " +
regionCode);
}
format.setFormat(formatPattern.item(0).getFirstChild().getNodeValue());
metadata.addNumberFormat(format);
}
}
NodeList intlNumberFormatElements = element.getElementsByTagName("intlNumberFormat");
int numOfIntlFormatElements = intlNumberFormatElements.getLength();
if (numOfIntlFormatElements > 0) {
for (int i = 0; i < numOfIntlFormatElements; i++) {
Element numberFormatElement = (Element) intlNumberFormatElements.item(i);
NumberFormat.Builder format = NumberFormat.newBuilder();
setLeadingDigitsPatterns(numberFormatElement, format);
format.setPattern(validateRE(numberFormatElement.getAttribute("pattern")));
NodeList formatPattern = numberFormatElement.getElementsByTagName("format");
if (formatPattern.getLength() != 1) {
LOGGER.log(Level.SEVERE,
"Only one format pattern for a numberFormat element should be defined.");
throw new RuntimeException("Invalid number of format patterns for country: " +
regionCode);
}
format.setFormat(validateRE(formatPattern.item(0).getFirstChild().getNodeValue()));
if (numberFormatElement.hasAttribute("carrierCodeFormattingRule")) {
format.setDomesticCarrierCodeFormattingRule(validateRE(
getDomesticCarrierCodeFormattingRuleFromElement(numberFormatElement,
nationalPrefix)));
} else {
format.setDomesticCarrierCodeFormattingRule(carrierCodeFormattingRule);
}
metadata.addIntlNumberFormat(format);
}
}
PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
generalDesc = processPhoneNumberDescElement(generalDesc, element, "generalDesc");
metadata.setGeneralDesc(generalDesc);
metadata.setFixedLine(processPhoneNumberDescElement(generalDesc, element, "fixedLine"));
metadata.setMobile(processPhoneNumberDescElement(generalDesc, element, "mobile"));
metadata.setTollFree(processPhoneNumberDescElement(generalDesc, element, "tollFree"));
metadata.setPremiumRate(processPhoneNumberDescElement(generalDesc, element, "premiumRate"));
metadata.setSharedCost(processPhoneNumberDescElement(generalDesc, element, "sharedCost"));
metadata.setVoip(processPhoneNumberDescElement(generalDesc, element, "voip"));
metadata.setPersonalNumber(processPhoneNumberDescElement(generalDesc, element,
"personalNumber"));
metadata.setPager(processPhoneNumberDescElement(generalDesc, element, "pager"));
metadata.setUan(processPhoneNumberDescElement(generalDesc, element, "uan"));
metadata.setNoInternationalDialling(processPhoneNumberDescElement(generalDesc, element,
"noInternationalDialling"));
if (metadata.getMobile().getNationalNumberPattern().equals(
metadata.getFixedLine().getNationalNumberPattern())) {
metadata.setSameMobileAndFixedLinePattern(true);
}
return metadata.build();
}
private static void setLeadingDigitsPatterns(Element numberFormatElement,
NumberFormat.Builder format) {
NodeList leadingDigitsPatternNodes = numberFormatElement.getElementsByTagName("leadingDigits");
int numOfLeadingDigitsPatterns = leadingDigitsPatternNodes.getLength();
if (numOfLeadingDigitsPatterns > 0) {
for (int i = 0; i < numOfLeadingDigitsPatterns; i++) {
format.addLeadingDigitsPattern(
validateRE((leadingDigitsPatternNodes.item(i)).getFirstChild().getNodeValue(), true));
}
}
}
private static String getNationalPrefixFormattingRuleFromElement(Element element,
String nationalPrefix) {
String nationalPrefixFormattingRule = element.getAttribute("nationalPrefixFormattingRule");
// Replace $NP with national prefix and $FG with the first group ($1).
nationalPrefixFormattingRule =
nationalPrefixFormattingRule.replaceFirst("\\$NP", nationalPrefix)
.replaceFirst("\\$FG", "\\$1");
return nationalPrefixFormattingRule;
}
private static String getDomesticCarrierCodeFormattingRuleFromElement(Element element,
String nationalPrefix) {
String carrierCodeFormattingRule = element.getAttribute("carrierCodeFormattingRule");
// Replace $FG with the first group ($1) and $NP with the national prefix.
carrierCodeFormattingRule = carrierCodeFormattingRule.replaceFirst("\\$FG", "\\$1")
.replaceFirst("\\$NP", nationalPrefix);
return carrierCodeFormattingRule;
}
/**
* Processes a phone number description element from the XML file and returns it as a
* PhoneNumberDesc. If the description element is a fixed line or mobile number, the general
* description will be used to fill in the whole element if necessary, or any components that are
* missing. For all other types, the general description will only be used to fill in missing
* components if the type has a partial definition. For example, if no "tollFree" element exists,
* we assume there are no toll free numbers for that locale, and return a phone number description
* with "NA" for both the national and possible number patterns.
*
* @param generalDesc a generic phone number description that will be used to fill in missing
* parts of the description
* @param countryElement the XML element representing all the country information
* @param numberType the name of the number type, corresponding to the appropriate tag in the XML
* file with information about that type
* @return complete description of that phone number type
*/
private static PhoneNumberDesc.Builder processPhoneNumberDescElement(
PhoneNumberDesc.Builder generalDesc,
Element countryElement,
String numberType) {
NodeList phoneNumberDescList = countryElement.getElementsByTagName(numberType);
PhoneNumberDesc.Builder numberDesc = PhoneNumberDesc.newBuilder();
if (phoneNumberDescList.getLength() == 0 &&
(!numberType.equals("fixedLine") && !numberType.equals("mobile") &&
!numberType.equals("generalDesc"))) {
numberDesc.setNationalNumberPattern("NA");
numberDesc.setPossibleNumberPattern("NA");
return numberDesc;
}
numberDesc.mergeFrom(generalDesc.build());
if (phoneNumberDescList.getLength() > 0) {
Element element = (Element) phoneNumberDescList.item(0);
NodeList possiblePattern = element.getElementsByTagName("possibleNumberPattern");
if (possiblePattern.getLength() > 0) {
numberDesc.setPossibleNumberPattern(
validateRE(possiblePattern.item(0).getFirstChild().getNodeValue(), true));
}
NodeList validPattern = element.getElementsByTagName("nationalNumberPattern");
if (validPattern.getLength() > 0) {
numberDesc.setNationalNumberPattern(
validateRE(validPattern.item(0).getFirstChild().getNodeValue(), true));
}
if (!liteBuild) {
NodeList exampleNumber = element.getElementsByTagName("exampleNumber");
if (exampleNumber.getLength() > 0) {
numberDesc.setExampleNumber(exampleNumber.item(0).getFirstChild().getNodeValue());
}
}
}
return numberDesc;
}
}

+ 44
- 0
tools/java/common/src/com/google/i18n/phonenumbers/tools/CopyrightNotice.java View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2011 Google Inc.
*
* 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.tools;
import java.util.Calendar;
/**
* Class containing the Apache copyright notice used by code generators.
*
* @author Philippe Liard
*/
public class CopyrightNotice {
public static final String TEXT =
"/*\n" +
" * Copyright (C) " + Calendar.getInstance().get(Calendar.YEAR) + " Google Inc.\n" +
" *\n" +
" * Licensed under the Apache License, Version 2.0 (the \"License\");\n" +
" * you may not use this file except in compliance with the License.\n" +
" * You may obtain a copy of the License at\n" +
" *\n" +
" * http://www.apache.org/licenses/LICENSE-2.0\n" +
" *\n" +
" * Unless required by applicable law or agreed to in writing, software\n" +
" * distributed under the License is distributed on an \"AS IS\" BASIS,\n" +
" * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" +
" * See the License for the specific language governing permissions and\n" +
" * limitations under the License.\n" +
" */\n";
}

+ 6
- 3
tools/java/cpp-build/pom.xml View File

@ -20,6 +20,7 @@
<build>
<sourceDirectory>src</sourceDirectory>
<testSourceDirectory>test</testSourceDirectory>
<plugins>
<plugin>
<version>2.3.2</version>
@ -64,6 +65,8 @@
<configuration>
<sources>
<source>generated/</source>
<!-- Also add ../common/src/ which contains BuildMetadataFromXml.java -->
<source>../common/src/</source>
</sources>
</configuration>
</execution>
@ -123,9 +126,9 @@
<dependencies>
<dependency>
<groupId>com.google.i18n.phonenumbers.tools</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>


+ 201
- 0
tools/java/cpp-build/src/com/google/i18n/phonenumbers/tools/BuildMetadataCppFromXml.java View File

@ -0,0 +1,201 @@
/*
* Copyright (C) 2011 Google Inc.
*
* 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.tools;
import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
/**
* This class generates the C++ code representation of the provided XML metadata file. It lets us
* embed metadata directly in a native binary. We link the object resulting from the compilation of
* the code emitted by this class with the C++ phonenumber library.
*
* @author Philippe Liard
*/
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;
// Either test_metadata or metadata.{cc,hh} depending on the value of the 'forTesting' command
// line parameter.
private String baseFilename;
// 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;
/**
* Package private setter used to inject the binary stream for testing purpose.
*/
void setBinaryStream(ByteArrayOutputStream stream) {
this.binaryStream = stream;
}
@Override
public String getCommandName() {
return "BuildMetadataCppFromXml";
}
/**
* 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.
*
* @return true if the generation succeeded.
*/
@Override
public boolean start() {
if (!parseCommandLine()) {
return false;
}
try {
generateBinaryFromXml();
openFiles();
emitHeader();
emitImplementation();
} catch (Exception 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, false);
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/%s.h", outputDir, baseFilename));
implFileOutputStream = new FileOutputStream(String.format("%s/%s.cc", outputDir, baseFilename));
}
/**
* Generates the header file containing the two function prototypes:
* <pre>
* int X_size();
* const void* X_get();
* </pre>
*
* with X: 'metadata' or 'test_metadata'.
*/
private void emitHeader() {
final PrintWriter pw = new PrintWriter(headerFileOutputStream);
pw.write(CopyrightNotice.TEXT);
final String guardName = String.format("EMBEDDED_DATA_%s_H_", baseFilename.toUpperCase());
pw.println("#ifndef " + guardName);
pw.println("#define " + guardName);
pw.println();
pw.println(String.format("int %s_size();", baseFilename));
pw.println(String.format("const void* %s_get();", baseFilename));
pw.println();
pw.println("#endif // " + guardName);
pw.close();
}
/**
* The next two methods generate the implementation file (.cc) containing the file data and the
* two function implementations:
*
* <pre>
* #include "X.h"
*
* static const unsigned char[] X_data = { .... };
*
* const void* X_get() {
* return X_data;
* }
*
* unsigned int X_size() {
* return sizeof(X_data) / sizeof(X_data[0]);
* }
* </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);
pw.write(CopyrightNotice.TEXT);
pw.println(String.format("#include \"%s.h\"", baseFilename));
pw.println();
pw.print(String.format("static const unsigned char %s_data[] = { ", baseFilename));
emitStaticArrayCode(pw);
pw.println(" };");
pw.println();
pw.println(String.format("int %s_size() {", baseFilename));
pw.println(String.format(" return sizeof(%s_data) / sizeof(%s_data[0]);",
baseFilename, baseFilename));
pw.println("}");
pw.println();
pw.println(String.format("const void* %s_get() {", baseFilename));
pw.println(String.format(" return %s_data;", baseFilename));
pw.println("}");
pw.close();
}
/**
* 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();
for (int i = 0; i < buf.length; i++) {
pw.printf("0x%02X, ", buf[i]);
}
pw.flush();
binaryStream.flush();
binaryStream.close();
}
private boolean parseCommandLine() {
final String[] args = getArgs();
if (args.length != 4) {
System.err.println(String.format("Usage: %s <inputXmlFile> <outputDir> <forTesting>",
getCommandName()));
return false;
}
// args[0] is the name of the command.
inputFilePath = args[1];
outputDir = args[2];
baseFilename = Boolean.parseBoolean(args[3]) ? "test_metadata" : "metadata";
return true;
}
}

+ 33
- 0
tools/java/cpp-build/src/com/google/i18n/phonenumbers/tools/EntryPoint.java View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2011 Google Inc.
*
* 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.tools;
/**
* Entry point class for C++ build tools.
*
* @author Philippe Liard
*/
public class EntryPoint {
public static void main(String[] args) {
boolean status = new CommandDispatcher(args, new Command[] {
new BuildMetadataCppFromXml()
}).start();
System.exit(status ? 0 : 1);
}
}

+ 57
- 0
tools/java/cpp-build/test/com/google/i18n/phonenumbers/tools/BuildMetadataCppFromXmlTest.java View File

@ -0,0 +1,57 @@
/*
* Copyright (C) 2011 Google Inc.
*
* 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.tools;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
/**
* Tests the BuildMetadataCppFromXml implementation to make sure it emits the expected code.
*/
public class BuildMetadataCppFromXmlTest {
@Test
public void emitStaticArrayCode() {
final int streamSize = 4;
try {
ByteArrayOutputStream stream = new ByteArrayOutputStream(streamSize);
stream.write(0xca);
stream.write(0xfe);
stream.write(0xba);
stream.write(0xbe);
ByteArrayOutputStream result = new ByteArrayOutputStream(streamSize);
PrintWriter printWriter = new PrintWriter(result);
BuildMetadataCppFromXml buildMetadataCppFromXml = new BuildMetadataCppFromXml();
buildMetadataCppFromXml.setBinaryStream(stream);
buildMetadataCppFromXml.emitStaticArrayCode(printWriter);
assertEquals("0xCA, 0xFE, 0xBA, 0xBE, ", result.toString());
} catch (IOException e) {
fail(e.getMessage());
}
}
}

+ 1
- 1
tools/java/pom.xml View File

@ -20,7 +20,7 @@
</licenses>
<modules>
<module>common</module>
<module>cpp-build</module>
</modules>
</project>

Loading…
Cancel
Save