From: jia.shao.peng Date: Wed, 27 Apr 2011 13:32:25 +0000 (+0000) Subject: TOOLS: Adding metadata code generation to C++ build tools. Patch contributed by phili... X-Git-Tag: upstream/5.3.2~336 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=16295a6deaeaedd8ed6acb2e82e17c04fcb82123;p=platform%2Fupstream%2Flibphonenumber.git TOOLS: Adding metadata code generation to C++ build tools. Patch contributed by philip.liard git-svn-id: http://libphonenumber.googlecode.com/svn/trunk@185 ee073f10-1060-11df-b6a4-87a95322a99c --- diff --git a/tools/java/common/pom.xml b/tools/java/common/pom.xml deleted file mode 100644 index 14a8962..0000000 --- a/tools/java/common/pom.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - 4.0.0 - - - tools - com.google.i18n.phonenumbers - 1.0-SNAPSHOT - - - com.google.i18n.phonenumbers.tools - common - 1.0-SNAPSHOT - Libphonenumber common library for build tools - - 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. - - - - src - - - 2.3.2 - org.apache.maven.plugins - maven-compiler-plugin - - 1.5 - 1.5 - - - - - - diff --git a/tools/java/common/src/com/google/i18n/phonenumbers/tools/BuildMetadataFromXml.java b/tools/java/common/src/com/google/i18n/phonenumbers/tools/BuildMetadataFromXml.java new file mode 100644 index 0000000..f3ad60a --- /dev/null +++ b/tools/java/common/src/com/google/i18n/phonenumbers/tools/BuildMetadataFromXml.java @@ -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> buildCountryCodeToRegionCodeMap( + PhoneMetadataCollection metadataCollection) { + Map> countryCodeToRegionCodeMap = + new TreeMap>(); + 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 listWithRegionCode = new ArrayList(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; + } +} diff --git a/tools/java/common/src/com/google/i18n/phonenumbers/tools/CopyrightNotice.java b/tools/java/common/src/com/google/i18n/phonenumbers/tools/CopyrightNotice.java new file mode 100644 index 0000000..7efd7d1 --- /dev/null +++ b/tools/java/common/src/com/google/i18n/phonenumbers/tools/CopyrightNotice.java @@ -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"; +} diff --git a/tools/java/cpp-build/pom.xml b/tools/java/cpp-build/pom.xml index efb08c0..9434853 100644 --- a/tools/java/cpp-build/pom.xml +++ b/tools/java/cpp-build/pom.xml @@ -20,6 +20,7 @@ src + test 2.3.2 @@ -64,6 +65,8 @@ generated/ + + ../common/src/ @@ -123,9 +126,9 @@ - com.google.i18n.phonenumbers.tools - common - 1.0-SNAPSHOT + junit + junit + 4.8.1 com.google.protobuf diff --git a/tools/java/cpp-build/src/com/google/i18n/phonenumbers/tools/BuildMetadataCppFromXml.java b/tools/java/cpp-build/src/com/google/i18n/phonenumbers/tools/BuildMetadataCppFromXml.java new file mode 100644 index 0000000..6c5bb21 --- /dev/null +++ b/tools/java/cpp-build/src/com/google/i18n/phonenumbers/tools/BuildMetadataCppFromXml.java @@ -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: + *
+   *   int X_size();
+   *   const void* X_get();
+   * 
+ * + * 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: + * + *
+   * #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]);
+   * }
+   * 
+ */ + + /** + * 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 ", + 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; + } +} diff --git a/tools/java/cpp-build/src/com/google/i18n/phonenumbers/tools/EntryPoint.java b/tools/java/cpp-build/src/com/google/i18n/phonenumbers/tools/EntryPoint.java new file mode 100644 index 0000000..47e4012 --- /dev/null +++ b/tools/java/cpp-build/src/com/google/i18n/phonenumbers/tools/EntryPoint.java @@ -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); + } +} diff --git a/tools/java/cpp-build/test/com/google/i18n/phonenumbers/tools/BuildMetadataCppFromXmlTest.java b/tools/java/cpp-build/test/com/google/i18n/phonenumbers/tools/BuildMetadataCppFromXmlTest.java new file mode 100644 index 0000000..658b390 --- /dev/null +++ b/tools/java/cpp-build/test/com/google/i18n/phonenumbers/tools/BuildMetadataCppFromXmlTest.java @@ -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()); + } + } +} diff --git a/tools/java/pom.xml b/tools/java/pom.xml index 0fb4b82..efd7b7d 100644 --- a/tools/java/pom.xml +++ b/tools/java/pom.xml @@ -20,7 +20,7 @@ - common + cpp-build