summaryrefslogtreecommitdiff
path: root/demo
diff options
context:
space:
mode:
Diffstat (limited to 'demo')
-rw-r--r--demo/.readme_res/App_Engine_Run_Config.pngbin0 -> 197152 bytes
-rw-r--r--demo/README21
-rw-r--r--demo/README.md50
-rw-r--r--demo/build.xml59
-rw-r--r--demo/pom.xml179
-rw-r--r--demo/src/com/google/phonenumbers/PhoneNumberParserServlet.java441
-rw-r--r--demo/src/main/java/com/google/phonenumbers/demo/InputServlet.java44
-rw-r--r--demo/src/main/java/com/google/phonenumbers/demo/ResultServlet.java144
-rw-r--r--demo/src/main/java/com/google/phonenumbers/demo/helper/WebHelper.java76
-rw-r--r--demo/src/main/java/com/google/phonenumbers/demo/render/ErrorRenderer.java49
-rw-r--r--demo/src/main/java/com/google/phonenumbers/demo/render/InputFormRenderer.java33
-rw-r--r--demo/src/main/java/com/google/phonenumbers/demo/render/LibPhoneNumberRenderer.java33
-rw-r--r--demo/src/main/java/com/google/phonenumbers/demo/render/ResultFileRenderer.java61
-rw-r--r--demo/src/main/java/com/google/phonenumbers/demo/render/ResultRenderer.java142
-rw-r--r--demo/src/main/resources/com/google/phonenumbers/demo/input_form.soy (renamed from demo/war/phonenumberparser.jsp)10
-rw-r--r--demo/src/main/resources/com/google/phonenumbers/demo/result.soy273
-rw-r--r--demo/src/main/resources/com/google/phonenumbers/demo/result_error.soy20
-rw-r--r--demo/src/main/resources/com/google/phonenumbers/demo/result_file.soy44
-rw-r--r--demo/src/main/webapp/WEB-INF/appengine-web.xml (renamed from demo/war/WEB-INF/appengine-web.xml)0
-rw-r--r--demo/src/main/webapp/WEB-INF/logging.properties (renamed from demo/war/WEB-INF/logging.properties)2
-rw-r--r--demo/src/main/webapp/WEB-INF/web.xml26
-rw-r--r--demo/src/main/webapp/favicon.ico (renamed from demo/war/favicon.ico)bin2417 -> 2417 bytes
-rw-r--r--demo/src/main/webapp/stylesheets/main.css (renamed from demo/war/stylesheets/main.css)0
-rw-r--r--demo/war/WEB-INF/lib/commons-fileupload-1.2.1.jarbin57779 -> 0 bytes
-rw-r--r--demo/war/WEB-INF/lib/commons-io-1.4.jarbin109043 -> 0 bytes
-rw-r--r--demo/war/WEB-INF/lib/commons-lang-2.6.jarbin284220 -> 0 bytes
-rw-r--r--demo/war/WEB-INF/lib/servlet-api-2.5.jarbin105112 -> 0 bytes
-rw-r--r--demo/war/WEB-INF/web.xml18
28 files changed, 1126 insertions, 599 deletions
diff --git a/demo/.readme_res/App_Engine_Run_Config.png b/demo/.readme_res/App_Engine_Run_Config.png
new file mode 100644
index 00000000..4c21985e
--- /dev/null
+++ b/demo/.readme_res/App_Engine_Run_Config.png
Binary files differ
diff --git a/demo/README b/demo/README
deleted file mode 100644
index ae42c5bc..00000000
--- a/demo/README
+++ /dev/null
@@ -1,21 +0,0 @@
-How to install the Appengine SDK to the Maven local repository?
-Note that this needs to be done only once.
- $ mvn gae:unpack
- Note that you don't need to manually download any SDK.
-
-How to generate the WAR?
- $ mvn package
- $ mvn war:war
- Note that this must be done before running the webapp locally and before
- deploying it to Appengine.
-
-How to run the webapp locally?
- $ mvn gae:run
-
-How to deploy the webapp to Appengine?
- $ mvn gae:update
-
-If you have made any changes to the code or pom.xml file of demo here,
-you may need to run Maven commands like ```mvn clean install -U```
-(especially one hierarchy above i.e at java/ folder) to resolve any
-dependency issues w.r.t new developement version.
diff --git a/demo/README.md b/demo/README.md
new file mode 100644
index 00000000..fa3a6d17
--- /dev/null
+++ b/demo/README.md
@@ -0,0 +1,50 @@
+# Running the demo
+
+## 1. Install Google Cloud SDK
+
+### Linux
+
+#### Install the Appengine SDK.
+
+`$ sudo apt-get install google-cloud-sdk-app-engine-java`
+
+### Mac
+
+#### Install the Google Cloud SDK.
+
+`$ brew install google-cloud-sdk`
+
+#### Use GCloud CLI to install the Java Appengine
+
+`$ gcloud components install app-engine-java`
+
+## 2. Add IDE Plugin
+
+### Intellij
+
+Install [Cloud Code Plugin for Intellij](https://plugins.jetbrains.com/plugin/8079-cloud-code)
+
+_If you have worked with the App Engine Plugin before,
+please [migrate](https://cloud.google.com/code/docs/intellij/migrate)._
+
+### Eclipse
+
+Install [Cloud Code Plugin for Eclipse](https://marketplace.eclipse.org/content/google-cloud-tools-eclipse)
+
+### Visual Studio Code
+
+Install [Cloud Code Plugin for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=GoogleCloudTools.cloudcode&ssr=false#overview)
+
+## 3. Run
+
+Most IDEs should autodetect your configuration after installing the plugin (and
+restart)
+
+![App Engine Run Config](.readme_res/App_Engine_Run_Config.png)
+
+## (Optional) Run Natively
+
+This is _not recommended_ with an IDE as you have to manage updating your
+artifacts yourself, it does not support live reload
+
+`$ mvn appengine:run`
diff --git a/demo/build.xml b/demo/build.xml
deleted file mode 100644
index 5cc93a78..00000000
--- a/demo/build.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<project>
- <property name="appengine-sdk.dir" location="../../appengine-java-sdk" />
-
- <import file="${appengine-sdk.dir}/config/user/ant-macros.xml" />
-
- <path id="project.classpath">
- <pathelement path="war/WEB-INF/classes" />
- <fileset dir="war/WEB-INF/lib">
- <include name="**/*.jar" />
- </fileset>
- <fileset dir="${appengine-sdk.dir}/lib">
- <include name="shared/**/*.jar" />
- </fileset>
- </path>
-
- <target name="copyjars" description="Copies the App Engine JARs to the WAR.">
- <copy todir="war/WEB-INF/lib" flatten="true">
- <fileset dir="${appengine-sdk.dir}/lib/user">
- <include name="**/*.jar" />
- </fileset>
- </copy>
- </target>
-
- <target name="compile" depends="copyjars"
- description="Compiles Java source and copies other source files to the WAR.">
- <mkdir dir="war/WEB-INF/classes" />
- <copy todir="war/WEB-INF/classes">
- <fileset dir="src">
- <exclude name="**/*.java" />
- </fileset>
- </copy>
- <javac srcdir="src" destdir="war/WEB-INF/classes" classpathref="project.classpath" debug="on"
- includeAntRuntime="false" source="1.8" target="1.8"/>
- </target>
-
- <target name="runserver" depends="compile" description="Starts the development server.">
- <dev_appserver war="war" />
- </target>
-
- <target name="update" depends="compile" description="Uploads the application to App Engine.">
- <appcfg action="update" war="war" />
- </target>
-
- <target name="rollback" depends="compile"
- description="Rolls back an interrupted application update.">
- <appcfg action="rollback" war="war" />
- </target>
-
- <target name="request_logs" description="Downloads log data from App Engine for the application.">
- <appcfg action="request_logs" war="war">
- <options>
- <arg value="--num_days=5"/>
- </options>
- <args>
- <arg value="logs.txt"/>
- </args>
- </appcfg>
- </target>
-</project>
diff --git a/demo/pom.xml b/demo/pom.xml
index 775474c8..4723727d 100644
--- a/demo/pom.xml
+++ b/demo/pom.xml
@@ -3,73 +3,52 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.googlecode.libphonenumber</groupId>
<artifactId>demo</artifactId>
- <version>8.12.46</version>
- <packaging>jar</packaging>
+ <version>8.13.2</version>
+ <packaging>war</packaging>
<url>https://github.com/google/libphonenumber/</url>
<parent>
<groupId>com.googlecode.libphonenumber</groupId>
<artifactId>libphonenumber-parent</artifactId>
- <version>8.12.46</version>
+ <version>8.13.2</version>
</parent>
<properties>
- <gae.version>1.9.32</gae.version>
+ <app.deploy.project>libphonenumber-hrd</app.deploy.project>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+ <maven.compiler.source>1.8</maven.compiler.source>
+ <maven.compiler.target>1.8</maven.compiler.target>
+ <maven.compiler.showDeprecation>true</maven.compiler.showDeprecation>
+ <archiveClasses>true</archiveClasses>
+ <soy.root>${project.basedir}/src/main/resources/com/google/phonenumbers/demo</soy.root>
+ <soy.srcs>${soy.root}/input_form.soy,${soy.root}/result.soy,${soy.root}/result_error.soy,${soy.root}/result_file.soy</soy.srcs>
+ <soy.parser.output>${project.build.directory}/generated-sources/com/google/phonenumbers/demo/template/</soy.parser.output>
+ <soy.compiler.output>${project.build.directory}/${project.build.finalName}/WEB-INF/lib/soy-templates.jar</soy.compiler.output>
+ <soy.package>com.google.phonenumbers.demo.template</soy.package>
</properties>
- <build>
- <sourceDirectory>src</sourceDirectory>
- <testSourceDirectory>test</testSourceDirectory>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>2.3.2</version>
- <configuration>
- <source>1.7</source>
- <target>1.7</target>
- <encoding>UTF-8</encoding>
- </configuration>
- </plugin>
- <plugin>
- <groupId>net.kindleit</groupId>
- <artifactId>maven-gae-plugin</artifactId>
- <version>0.9.1</version>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-war-plugin</artifactId>
- <version>2.1.1</version>
- <configuration>
- <warSourceDirectory>war</warSourceDirectory>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.mortbay.jetty</groupId>
- <artifactId>maven-jetty-plugin</artifactId>
- <version>6.1.10</version>
- <configuration>
- <webAppSourceDirectory>webapp</webAppSourceDirectory>
- <scanIntervalSeconds>10</scanIntervalSeconds>
- <webAppConfig>
- <contextPath>/</contextPath>
- </webAppConfig>
- <connectors>
- <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
- <port>8080</port>
- <maxIdleTime>60000</maxIdleTime>
- </connector>
- </connectors>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
+ <prerequisites>
+ <maven>3.5</maven>
+ </prerequisites>
<dependencies>
+ <!-- Compile/runtime dependencies -->
+ <dependency>
+ <artifactId>soy</artifactId>
+ <groupId>com.google.template</groupId>
+ <version>2022-07-20</version>
+ </dependency>
+ <dependency>
+ <artifactId>appengine-maven-plugin</artifactId>
+ <groupId>com.google.cloud.tools</groupId>
+ <version>2.4.4</version>
+ </dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
+ <type>jar</type>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
@@ -79,7 +58,7 @@
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
- <version>1.3.3</version>
+ <version>1.4</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
@@ -89,18 +68,106 @@
<dependency>
<groupId>com.googlecode.libphonenumber</groupId>
<artifactId>libphonenumber</artifactId>
- <version>8.12.46</version>
+ <version>8.13.2</version>
</dependency>
<dependency>
<groupId>com.googlecode.libphonenumber</groupId>
<artifactId>geocoder</artifactId>
- <version>2.182</version>
+ <version>2.196</version>
</dependency>
<dependency>
<groupId>com.googlecode.libphonenumber</groupId>
<artifactId>carrier</artifactId>
- <version>1.172</version>
+ <version>1.186</version>
</dependency>
</dependencies>
+ <build>
+ <!-- for hot reload of the web application-->
+ <outputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/classes</outputDirectory>
+ <plugins>
+ <plugin>
+ <groupId>com.google.cloud.tools</groupId>
+ <artifactId>appengine-maven-plugin</artifactId>
+ <version>2.4.4</version>
+ <configuration>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>8</source>
+ <target>8</target>
+ </configuration>
+ <groupId>org.apache.maven.plugins</groupId>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>3.3.3</version>
+ <executions>
+ <execution>
+ <!-- soy-parser generates template classes in
+ com.google.phonenumbers.demo.template package -->
+ <id>soy-parser</id>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ <phase>generate-sources</phase>
+ <configuration>
+ <executable>java</executable>
+ <arguments>
+ <argument>-classpath</argument>
+ <classpath />
+ <argument>com.google.template.soy.SoyParseInfoGenerator</argument>
+ <argument>--generateInvocationBuilders</argument>
+ <argument>--javaPackage</argument>
+ <argument>${soy.package}</argument>
+ <argument>--javaClassNameSource</argument>
+ <argument>filename</argument>
+ <argument>--srcs</argument>
+ <argument>${soy.srcs}</argument>
+ <argument>--outputDirectory</argument>
+ <argument>${soy.parser.output}</argument>
+ </arguments>
+ <sourceRoot>${soy.parser.output}</sourceRoot>
+ </configuration>
+ </execution>
+ <execution>
+ <!-- soy-compiler compiles templates so that we can use SoySauce
+ instead of Tofu. -->
+ <id>soy-compiler</id>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ <phase>verify</phase>
+ <configuration>
+ <executable>java</executable>
+ <arguments>
+ <argument>-classpath</argument>
+ <classpath />
+ <argument>com.google.template.soy.SoyToJbcSrcCompiler</argument>
+ <argument>--srcs</argument>
+ <argument>${soy.srcs}</argument>
+ <argument>--output</argument>
+ <argument>${soy.compiler.output}</argument>
+ </arguments>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>3.4.1</version>
+ <configuration>
+ <!-- SoyInfoParser does not generate valid javadoc and
+ <excludePackageNames> does not work for /generated-sources for
+ some reason-->
+ <failOnError>false</failOnError>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
</project>
+
diff --git a/demo/src/com/google/phonenumbers/PhoneNumberParserServlet.java b/demo/src/com/google/phonenumbers/PhoneNumberParserServlet.java
deleted file mode 100644
index 40f0fa8f..00000000
--- a/demo/src/com/google/phonenumbers/PhoneNumberParserServlet.java
+++ /dev/null
@@ -1,441 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- *
- * @author Shaopeng Jia
- */
-
-package com.google.phonenumbers;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.Locale.ENGLISH;
-
-import com.google.i18n.phonenumbers.AsYouTypeFormatter;
-import com.google.i18n.phonenumbers.NumberParseException;
-import com.google.i18n.phonenumbers.PhoneNumberToCarrierMapper;
-import com.google.i18n.phonenumbers.PhoneNumberToTimeZonesMapper;
-import com.google.i18n.phonenumbers.PhoneNumberUtil;
-import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
-import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberType;
-import com.google.i18n.phonenumbers.PhoneNumberUtil.ValidationResult;
-import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
-import com.google.i18n.phonenumbers.ShortNumberInfo;
-import com.google.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import java.util.Locale;
-import java.util.StringTokenizer;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.apache.commons.fileupload.FileItemIterator;
-import org.apache.commons.fileupload.FileItemStream;
-import org.apache.commons.fileupload.FileUploadException;
-import org.apache.commons.fileupload.servlet.ServletFileUpload;
-import org.apache.commons.fileupload.util.Streams;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang.StringEscapeUtils;
-
-/**
- * A servlet that accepts requests that contain strings representing a phone number and a default
- * country, and responds with results from parsing, validating and formatting the number. The
- * default country is a two-letter region code representing the country that we are expecting the
- * number to be from.
- */
-@SuppressWarnings("serial")
-public class PhoneNumberParserServlet extends HttpServlet {
- private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
- private ShortNumberInfo shortInfo = ShortNumberInfo.getInstance();
-
- public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
- String phoneNumber = null;
- String defaultCountry = null;
- String languageCode = "en"; // Default languageCode to English if nothing is entered.
- String regionCode = "";
- String fileContents = null;
- ServletFileUpload upload = new ServletFileUpload();
- upload.setSizeMax(50000);
- try {
- FileItemIterator iterator = upload.getItemIterator(req);
- while (iterator.hasNext()) {
- FileItemStream item = iterator.next();
- InputStream in = item.openStream();
- if (item.isFormField()) {
- String fieldName = item.getFieldName();
- if (fieldName.equals("phoneNumber")) {
- phoneNumber = Streams.asString(in, UTF_8.name());
- } else if (fieldName.equals("defaultCountry")) {
- defaultCountry = Streams.asString(in).toUpperCase();
- } else if (fieldName.equals("languageCode")) {
- String languageEntered = Streams.asString(in).toLowerCase();
- if (languageEntered.length() > 0) {
- languageCode = languageEntered;
- }
- } else if (fieldName.equals("regionCode")) {
- regionCode = Streams.asString(in).toUpperCase();
- }
- } else {
- try {
- fileContents = IOUtils.toString(in);
- } finally {
- IOUtils.closeQuietly(in);
- }
- }
- }
- } catch (FileUploadException e1) {
- e1.printStackTrace();
- }
-
- StringBuilder output;
- resp.setContentType("text/html");
- resp.setCharacterEncoding(UTF_8.name());
- if (fileContents == null || fileContents.length() == 0) {
- // Redirect to a URL with the given input encoded in the query parameters.
- Locale geocodingLocale = new Locale(languageCode, regionCode);
- resp.sendRedirect(
- getPermaLinkURL(phoneNumber, defaultCountry, geocodingLocale, false /* absoluteURL */));
- } else {
- resp.getWriter().println(getOutputForFile(defaultCountry, fileContents));
- }
- }
-
- /** Handle the get request to get information about a number based on query parameters. */
- public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
- String phoneNumber = req.getParameter("number");
- if (phoneNumber == null) {
- phoneNumber = "";
- }
- String defaultCountry = req.getParameter("country");
- if (defaultCountry == null) {
- defaultCountry = "";
- }
- String geocodingParam = req.getParameter("geocodingLocale");
- Locale geocodingLocale;
- if (geocodingParam == null) {
- geocodingLocale = ENGLISH; // Default languageCode to English if nothing is entered.
- } else {
- geocodingLocale = Locale.forLanguageTag(geocodingParam);
- }
- resp.setContentType("text/html");
- resp.setCharacterEncoding(UTF_8.name());
- resp.getWriter()
- .println(getOutputForSingleNumber(phoneNumber, defaultCountry, geocodingLocale));
- }
-
- private StringBuilder getOutputForFile(String defaultCountry, String fileContents) {
- StringBuilder output =
- new StringBuilder(
- "<HTML><HEAD><TITLE>Results generated from phone numbers in the file provided:"
- + "</TITLE></HEAD><BODY>");
- output.append("<TABLE align=center border=1>");
- output.append("<TH align=center>ID</TH>");
- output.append("<TH align=center>Raw phone number</TH>");
- output.append("<TH align=center>Pretty formatting</TH>");
- output.append("<TH align=center>International format</TH>");
-
- int phoneNumberId = 0;
- StringTokenizer tokenizer = new StringTokenizer(fileContents, ",");
- while (tokenizer.hasMoreTokens()) {
- String numberStr = tokenizer.nextToken();
- phoneNumberId++;
- output.append("<TR>");
- output.append("<TD align=center>").append(phoneNumberId).append(" </TD> \n");
- output
- .append("<TD align=center>")
- .append(StringEscapeUtils.escapeHtml(numberStr))
- .append(" </TD> \n");
- try {
- PhoneNumber number = phoneUtil.parseAndKeepRawInput(numberStr, defaultCountry);
- boolean isNumberValid = phoneUtil.isValidNumber(number);
- String prettyFormat =
- isNumberValid ? phoneUtil.formatInOriginalFormat(number, defaultCountry) : "invalid";
- String internationalFormat =
- isNumberValid ? phoneUtil.format(number, PhoneNumberFormat.INTERNATIONAL) : "invalid";
-
- output
- .append("<TD align=center>")
- .append(StringEscapeUtils.escapeHtml(prettyFormat))
- .append(" </TD> \n");
- output
- .append("<TD align=center>")
- .append(StringEscapeUtils.escapeHtml(internationalFormat))
- .append(" </TD> \n");
- } catch (NumberParseException e) {
- output
- .append("<TD align=center colspan=2>")
- .append(StringEscapeUtils.escapeHtml(e.toString()))
- .append(" </TD> \n");
- }
- output.append("</TR>");
- }
- output.append("</BODY></HTML>");
- return output;
- }
-
- private void appendLine(String title, String data, StringBuilder output) {
- output.append("<TR>");
- output.append("<TH>").append(title).append("</TH>");
- output.append("<TD>").append(data.length() > 0 ? data : "&nbsp;").append("</TD>");
- output.append("</TR>");
- }
-
- /** Returns a stable URL pointing to the result page for the given input. */
- private String getPermaLinkURL(
- String phoneNumber, String defaultCountry, Locale geocodingLocale, boolean absoluteURL) {
- // If absoluteURL is false, generate a relative path. Otherwise, produce an absolute URL.
- StringBuilder permaLink =
- new StringBuilder(
- absoluteURL
- ? "http://libphonenumber.appspot.com/phonenumberparser"
- : "/phonenumberparser");
- try {
- permaLink.append(
- "?number=" + URLEncoder.encode(phoneNumber != null ? phoneNumber : "", UTF_8.name()));
- if (defaultCountry != null && !defaultCountry.isEmpty()) {
- permaLink.append("&country=" + URLEncoder.encode(defaultCountry, UTF_8.name()));
- }
- if (!geocodingLocale.getLanguage().equals(ENGLISH.getLanguage())
- || !geocodingLocale.getCountry().isEmpty()) {
- permaLink.append(
- "&geocodingLocale=" + URLEncoder.encode(geocodingLocale.toLanguageTag(), UTF_8.name()));
- }
- } catch (UnsupportedEncodingException e) {
- // UTF-8 is guaranteed in Java, so this should be impossible.
- throw new AssertionError(e);
- }
- return permaLink.toString();
- }
-
- private static final String NEW_ISSUE_BASE_URL =
- "https://issuetracker.google.com/issues/new?component=192347&title=";
-
- /** Returns a link to create a new github issue with the relevant information. */
- private String getNewIssueLink(
- String phoneNumber, String defaultCountry, Locale geocodingLocale) {
- boolean hasDefaultCountry = !defaultCountry.isEmpty() && defaultCountry != "ZZ";
- String issueTitle =
- "Validation issue with "
- + phoneNumber
- + (hasDefaultCountry ? " (" + defaultCountry + ")" : "");
-
- String newIssueLink = NEW_ISSUE_BASE_URL;
- try {
- newIssueLink += URLEncoder.encode(issueTitle, UTF_8.name());
- } catch (UnsupportedEncodingException e) {
- // UTF-8 is guaranteed in Java, so this should be impossible.
- throw new AssertionError(e);
- }
- return newIssueLink;
- }
-
- /**
- * The defaultCountry here is used for parsing phoneNumber. The geocodingLocale is used to specify
- * the language used for displaying the area descriptions generated from phone number geocoding.
- */
- private StringBuilder getOutputForSingleNumber(
- String phoneNumber, String defaultCountry, Locale geocodingLocale) {
- StringBuilder output = new StringBuilder("<HTML><HEAD>");
- output.append("<LINK type=\"text/css\" rel=\"stylesheet\" href=\"/stylesheets/main.css\" />");
- output.append("</HEAD>");
- output.append("<BODY>");
- output.append("Phone Number entered: " + StringEscapeUtils.escapeHtml(phoneNumber) + "<BR>");
- output.append(
- "defaultCountry entered: " + StringEscapeUtils.escapeHtml(defaultCountry) + "<BR>");
- output.append(
- "Language entered: "
- + StringEscapeUtils.escapeHtml(geocodingLocale.toLanguageTag())
- + "<BR>");
- try {
- PhoneNumber number = phoneUtil.parseAndKeepRawInput(phoneNumber, defaultCountry);
- output.append("<DIV>");
- output.append("<TABLE border=1>");
- output.append("<TR><TD colspan=2>Parsing Result (parseAndKeepRawInput())</TD></TR>");
-
- appendLine("country_code", Integer.toString(number.getCountryCode()), output);
- appendLine("national_number", Long.toString(number.getNationalNumber()), output);
- appendLine("extension", number.getExtension(), output);
- appendLine("country_code_source", number.getCountryCodeSource().toString(), output);
- appendLine("italian_leading_zero", Boolean.toString(number.isItalianLeadingZero()), output);
- appendLine("raw_input", number.getRawInput(), output);
- output.append("</TABLE>");
- output.append("</DIV>");
-
- boolean isPossible = phoneUtil.isPossibleNumber(number);
- boolean isNumberValid = phoneUtil.isValidNumber(number);
- PhoneNumberType numberType = phoneUtil.getNumberType(number);
- boolean hasDefaultCountry = !defaultCountry.isEmpty() && defaultCountry != "ZZ";
-
- output.append("<DIV>");
- output.append("<TABLE border=1>");
- output.append("<TR><TD colspan=2>Validation Results</TD></TR>");
- appendLine("Result from isPossibleNumber()", Boolean.toString(isPossible), output);
- if (isPossible) {
- if (phoneUtil.isPossibleNumberWithReason(number)
- == ValidationResult.IS_POSSIBLE_LOCAL_ONLY) {
- appendLine("Result from isPossibleNumberWithReason()", "IS_POSSIBLE_LOCAL_ONLY", output);
- output.append(
- "<TR><TD colspan=2>Number is considered invalid as it is "
- + "not a possible national number.</TD></TR>");
- } else {
- appendLine("Result from isValidNumber()", Boolean.toString(isNumberValid), output);
- if (isNumberValid && hasDefaultCountry) {
- appendLine(
- "Result from isValidNumberForRegion()",
- Boolean.toString(phoneUtil.isValidNumberForRegion(number, defaultCountry)),
- output);
- }
- String region = phoneUtil.getRegionCodeForNumber(number);
- appendLine("Phone Number region", region == null ? "" : region, output);
- appendLine("Result from getNumberType()", numberType.toString(), output);
- }
- } else {
- appendLine(
- "Result from isPossibleNumberWithReason()",
- phoneUtil.isPossibleNumberWithReason(number).toString(),
- output);
- output.append(
- "<TR><TD colspan=2>Note: Numbers that are not possible have type UNKNOWN,"
- + " an unknown region, and are considered invalid.</TD></TR>");
- }
- output.append("</TABLE>");
- output.append("</DIV>");
-
- if (!isNumberValid) {
- output.append("<DIV>");
- output.append("<TABLE border=1>");
- output.append("<TR><TD colspan=2>Short Number Results</TD></TR>");
- boolean isPossibleShort = shortInfo.isPossibleShortNumber(number);
- appendLine(
- "Result from isPossibleShortNumber()", Boolean.toString(isPossibleShort), output);
- if (isPossibleShort) {
- appendLine(
- "Result from isValidShortNumber()",
- Boolean.toString(shortInfo.isValidShortNumber(number)),
- output);
- if (hasDefaultCountry) {
- boolean isPossibleShortForRegion =
- shortInfo.isPossibleShortNumberForRegion(number, defaultCountry);
- appendLine(
- "Result from isPossibleShortNumberForRegion()",
- Boolean.toString(isPossibleShortForRegion),
- output);
- if (isPossibleShortForRegion) {
- appendLine(
- "Result from isValidShortNumberForRegion()",
- Boolean.toString(shortInfo.isValidShortNumberForRegion(number, defaultCountry)),
- output);
- }
- }
- }
- output.append("</TABLE>");
- output.append("</DIV>");
- }
-
- output.append("<DIV>");
- output.append("<TABLE border=1>");
- output.append("<TR><TD colspan=2>Formatting Results</TD></TR>");
- appendLine(
- "E164 format",
- isNumberValid ? phoneUtil.format(number, PhoneNumberFormat.E164) : "invalid",
- output);
- appendLine(
- "Original format", phoneUtil.formatInOriginalFormat(number, defaultCountry), output);
- appendLine("National format", phoneUtil.format(number, PhoneNumberFormat.NATIONAL), output);
- appendLine(
- "International format",
- isNumberValid ? phoneUtil.format(number, PhoneNumberFormat.INTERNATIONAL) : "invalid",
- output);
- appendLine(
- "Out-of-country format from US",
- isNumberValid ? phoneUtil.formatOutOfCountryCallingNumber(number, "US") : "invalid",
- output);
- appendLine(
- "Out-of-country format from CH",
- isNumberValid ? phoneUtil.formatOutOfCountryCallingNumber(number, "CH") : "invalid",
- output);
- output.append("</TABLE>");
- output.append("</DIV>");
-
- AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter(defaultCountry);
- int rawNumberLength = phoneNumber.length();
- output.append("<DIV>");
- output.append("<TABLE border=1>");
- output.append("<TR><TD colspan=2>AsYouTypeFormatter Results</TD></TR>");
- for (int i = 0; i < rawNumberLength; i++) {
- // Note this doesn't handle supplementary characters, but it shouldn't be a big deal as
- // there are no dial-pad characters in the supplementary range.
- char inputChar = phoneNumber.charAt(i);
- appendLine(
- "Char entered: '" + inputChar + "' Output: ", formatter.inputDigit(inputChar), output);
- }
- output.append("</TABLE>");
- output.append("</DIV>");
-
- if (isNumberValid) {
- output.append("<DIV>");
- output.append("<TABLE border=1>");
- output.append("<TR><TD colspan=2>PhoneNumberOfflineGeocoder Results</TD></TR>");
- appendLine(
- "Location",
- PhoneNumberOfflineGeocoder.getInstance()
- .getDescriptionForNumber(number, geocodingLocale),
- output);
- output.append("</TABLE>");
- output.append("</DIV>");
-
- output.append("<DIV>");
- output.append("<TABLE border=1>");
- output.append("<TR><TD colspan=2>PhoneNumberToTimeZonesMapper Results</TD></TR>");
- appendLine(
- "Time zone(s)",
- PhoneNumberToTimeZonesMapper.getInstance().getTimeZonesForNumber(number).toString(),
- output);
- output.append("</TABLE>");
- output.append("</DIV>");
-
- if (numberType == PhoneNumberType.MOBILE
- || numberType == PhoneNumberType.FIXED_LINE_OR_MOBILE
- || numberType == PhoneNumberType.PAGER) {
- output.append("<DIV>");
- output.append("<TABLE border=1>");
- output.append("<TR><TD colspan=2>PhoneNumberToCarrierMapper Results</TD></TR>");
- appendLine(
- "Carrier",
- PhoneNumberToCarrierMapper.getInstance().getNameForNumber(number, geocodingLocale),
- output);
- output.append("</TABLE>");
- output.append("</DIV>");
- }
- }
-
- String newIssueLink = getNewIssueLink(phoneNumber, defaultCountry, geocodingLocale);
- String guidelinesLink =
- "https://github.com/google/libphonenumber/blob/master/CONTRIBUTING.md";
- output.append(
- "<b style=\"color:red\">File an issue</b>: by clicking on "
- + "<a target=\"_blank\" href=\""
- + newIssueLink
- + "\">this link</a>, I confirm that I "
- + "have read the <a target=\"_blank\" href=\""
- + guidelinesLink
- + "\">contributor's guidelines</a>.");
- } catch (NumberParseException e) {
- output.append(StringEscapeUtils.escapeHtml(e.toString()));
- }
- output.append("</BODY></HTML>");
- return output;
- }
-}
diff --git a/demo/src/main/java/com/google/phonenumbers/demo/InputServlet.java b/demo/src/main/java/com/google/phonenumbers/demo/InputServlet.java
new file mode 100644
index 00000000..ebb93e20
--- /dev/null
+++ b/demo/src/main/java/com/google/phonenumbers/demo/InputServlet.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 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.
+ *
+ * @author Shaopeng Jia
+ */
+
+package com.google.phonenumbers.demo;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.phonenumbers.demo.render.InputFormRenderer;
+import java.io.IOException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * A servlet that accepts requests that contain strings representing a phone number and a default
+ * country, and responds with results from parsing, validating and formatting the number. The
+ * default country is a two-letter region code representing the country that we are expecting the
+ * number to be from.
+ */
+@SuppressWarnings("serial")
+public class InputServlet extends HttpServlet {
+
+ /** Handle the get request to get information about a number based on query parameters. */
+ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ resp.setContentType("text/html");
+ resp.setCharacterEncoding(UTF_8.name());
+ resp.getWriter().println(new InputFormRenderer().genHtml());
+ }
+}
diff --git a/demo/src/main/java/com/google/phonenumbers/demo/ResultServlet.java b/demo/src/main/java/com/google/phonenumbers/demo/ResultServlet.java
new file mode 100644
index 00000000..04dc0af7
--- /dev/null
+++ b/demo/src/main/java/com/google/phonenumbers/demo/ResultServlet.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2022 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.
+ *
+ * @author Shaopeng Jia
+ */
+
+package com.google.phonenumbers.demo;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Locale.ENGLISH;
+
+import com.google.i18n.phonenumbers.NumberParseException;
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
+import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
+import com.google.phonenumbers.demo.helper.WebHelper;
+import com.google.phonenumbers.demo.render.ErrorRenderer;
+import com.google.phonenumbers.demo.render.ResultFileRenderer;
+import com.google.phonenumbers.demo.render.ResultRenderer;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Locale;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.fileupload.FileItemIterator;
+import org.apache.commons.fileupload.FileItemStream;
+import org.apache.commons.fileupload.FileUploadException;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.apache.commons.fileupload.util.Streams;
+import org.apache.commons.io.IOUtils;
+
+/**
+ * A servlet that accepts requests that contain strings representing a phone number and a default
+ * country, and responds with results from parsing, validating and formatting the number. The
+ * default country is a two-letter region code representing the country that we are expecting the
+ * number to be from.
+ */
+@SuppressWarnings("serial")
+public class ResultServlet extends HttpServlet {
+
+ private final PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
+
+ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ String phoneNumber = null;
+ String defaultCountry = null;
+ String languageCode = "en"; // Default languageCode to English if nothing is entered.
+ String regionCode = "";
+ String fileContents = null;
+ ServletFileUpload upload = new ServletFileUpload();
+ upload.setSizeMax(50000);
+ try {
+ FileItemIterator iterator = upload.getItemIterator(req);
+ while (iterator.hasNext()) {
+ FileItemStream item = iterator.next();
+ InputStream in = item.openStream();
+ if (item.isFormField()) {
+ String fieldName = item.getFieldName();
+ if (fieldName.equals("phoneNumber")) {
+ phoneNumber = Streams.asString(in, UTF_8.name());
+ } else if (fieldName.equals("defaultCountry")) {
+ defaultCountry = Streams.asString(in).toUpperCase();
+ } else if (fieldName.equals("languageCode")) {
+ String languageEntered = Streams.asString(in).toLowerCase();
+ if (languageEntered.length() > 0) {
+ languageCode = languageEntered;
+ }
+ } else if (fieldName.equals("regionCode")) {
+ regionCode = Streams.asString(in).toUpperCase();
+ }
+ } else {
+ try {
+ fileContents = IOUtils.toString(in);
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ }
+ }
+ } catch (FileUploadException e1) {
+ e1.printStackTrace();
+ }
+
+ resp.setContentType("text/html");
+ resp.setCharacterEncoding(UTF_8.name());
+ if (fileContents == null || fileContents.length() == 0) {
+ // Redirect to a URL with the given input encoded in the query parameters.
+ Locale geocodingLocale = new Locale(languageCode, regionCode);
+ resp.sendRedirect(
+ WebHelper.getPermaLinkURL(
+ phoneNumber, defaultCountry, geocodingLocale, false /* absoluteURL */));
+ } else {
+ resp.getWriter().println(new ResultFileRenderer(defaultCountry, fileContents).genHtml());
+ }
+ }
+
+ /** Handle the get request to get information about a number based on query parameters. */
+ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ String phoneNumber = req.getParameter("number");
+ if (phoneNumber == null) {
+ phoneNumber = "";
+ }
+ String defaultCountry = req.getParameter("country");
+ if (defaultCountry == null) {
+ defaultCountry = "";
+ }
+ String geocodingParam = req.getParameter("geocodingLocale");
+ Locale geocodingLocale;
+ if (geocodingParam == null) {
+ geocodingLocale = ENGLISH; // Default languageCode to English if nothing is entered.
+ } else {
+ geocodingLocale = Locale.forLanguageTag(geocodingParam);
+ }
+ resp.setContentType("text/html");
+ resp.setCharacterEncoding(UTF_8.name());
+ resp.getWriter()
+ .println(getOutputForSingleNumber(phoneNumber, defaultCountry, geocodingLocale));
+ }
+
+ /**
+ * The defaultCountry here is used for parsing phoneNumber. The geocodingLocale is used to specify
+ * the language used for displaying the area descriptions generated from phone number geocoding.
+ */
+ private String getOutputForSingleNumber(
+ String phoneNumber, String defaultCountry, Locale geocodingLocale) {
+ try {
+ PhoneNumber number = phoneUtil.parseAndKeepRawInput(phoneNumber, defaultCountry);
+ return new ResultRenderer(phoneNumber, defaultCountry, geocodingLocale, number).genHtml();
+ } catch (NumberParseException e) {
+ return new ErrorRenderer(phoneNumber, defaultCountry, geocodingLocale, e.toString())
+ .genHtml();
+ }
+ }
+}
diff --git a/demo/src/main/java/com/google/phonenumbers/demo/helper/WebHelper.java b/demo/src/main/java/com/google/phonenumbers/demo/helper/WebHelper.java
new file mode 100644
index 00000000..6430bb88
--- /dev/null
+++ b/demo/src/main/java/com/google/phonenumbers/demo/helper/WebHelper.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 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.
+ *
+ * @author Shaopeng Jia
+ */
+
+package com.google.phonenumbers.demo.helper;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Locale.ENGLISH;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.Locale;
+
+public class WebHelper {
+ private static final String NEW_ISSUE_BASE_URL =
+ "https://issuetracker.google.com/issues/new?component=192347&title=";
+ /** Returns a stable URL pointing to the result page for the given input. */
+ public static String getPermaLinkURL(
+ String phoneNumber, String defaultCountry, Locale geocodingLocale, boolean absoluteURL) {
+ // If absoluteURL is false, generate a relative path. Otherwise, produce an absolute URL.
+ StringBuilder permaLink =
+ new StringBuilder(
+ absoluteURL
+ ? "http://libphonenumber.appspot.com/phonenumberparser"
+ : "/phonenumberparser");
+ try {
+ permaLink.append(
+ "?number=" + URLEncoder.encode(phoneNumber != null ? phoneNumber : "", UTF_8.name()));
+ if (defaultCountry != null && !defaultCountry.isEmpty()) {
+ permaLink.append("&country=" + URLEncoder.encode(defaultCountry, UTF_8.name()));
+ }
+ if (!geocodingLocale.getLanguage().equals(ENGLISH.getLanguage())
+ || !geocodingLocale.getCountry().isEmpty()) {
+ permaLink.append(
+ "&geocodingLocale=" + URLEncoder.encode(geocodingLocale.toLanguageTag(), UTF_8.name()));
+ }
+ } catch (UnsupportedEncodingException e) {
+ // UTF-8 is guaranteed in Java, so this should be impossible.
+ throw new AssertionError(e);
+ }
+ return permaLink.toString();
+ }
+
+ /** Returns a link to create a new github issue with the relevant information. */
+ public static String getNewIssueLink(
+ String phoneNumber, String defaultCountry, Locale geocodingLocale) {
+ boolean hasDefaultCountry = !defaultCountry.isEmpty() && defaultCountry != "ZZ";
+ String issueTitle =
+ "Validation issue with "
+ + phoneNumber
+ + (hasDefaultCountry ? " (" + defaultCountry + ")" : "");
+
+ String newIssueLink = NEW_ISSUE_BASE_URL;
+ try {
+ newIssueLink += URLEncoder.encode(issueTitle, UTF_8.name());
+ } catch (UnsupportedEncodingException e) {
+ // UTF-8 is guaranteed in Java, so this should be impossible.
+ throw new AssertionError(e);
+ }
+ return newIssueLink;
+ }
+}
diff --git a/demo/src/main/java/com/google/phonenumbers/demo/render/ErrorRenderer.java b/demo/src/main/java/com/google/phonenumbers/demo/render/ErrorRenderer.java
new file mode 100644
index 00000000..9cff392b
--- /dev/null
+++ b/demo/src/main/java/com/google/phonenumbers/demo/render/ErrorRenderer.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 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.
+ *
+ * @author Tobias Rogg
+ */
+
+package com.google.phonenumbers.demo.render;
+
+import com.google.phonenumbers.demo.template.ResultErrorTemplates;
+import com.google.phonenumbers.demo.template.ResultErrorTemplates.Error;
+import java.util.Locale;
+
+public class ErrorRenderer extends LibPhoneNumberRenderer<Error> {
+ private final String phoneNumber;
+ private final String defaultCountry;
+ private final Locale geocodingLocale;
+ private final String error;
+
+ public ErrorRenderer(
+ String phoneNumber, String defaultCountry, Locale geocodingLocale, String error) {
+ this.phoneNumber = phoneNumber;
+ this.defaultCountry = defaultCountry;
+ this.geocodingLocale = geocodingLocale;
+ this.error = error;
+ }
+
+ @Override
+ public String genHtml() {
+ return super.render(
+ ResultErrorTemplates.Error.builder()
+ .setPhoneNumber(phoneNumber)
+ .setDefaultCountry(defaultCountry)
+ .setGeocodingLocale(geocodingLocale.toLanguageTag())
+ .setError(error)
+ .build());
+ }
+}
diff --git a/demo/src/main/java/com/google/phonenumbers/demo/render/InputFormRenderer.java b/demo/src/main/java/com/google/phonenumbers/demo/render/InputFormRenderer.java
new file mode 100644
index 00000000..77e388a1
--- /dev/null
+++ b/demo/src/main/java/com/google/phonenumbers/demo/render/InputFormRenderer.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 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.
+ *
+ * @author Tobias Rogg
+ */
+
+package com.google.phonenumbers.demo.render;
+
+import com.google.phonenumbers.demo.template.InputFormTemplates;
+import com.google.phonenumbers.demo.template.InputFormTemplates.InputForm;
+
+public class InputFormRenderer extends LibPhoneNumberRenderer<InputForm> {
+
+ @Override
+ public String genHtml() {
+ return super.render(
+ InputFormTemplates.InputForm.builder()
+ .setWelcomeTitle("Phone Number Parser Demo for LibPhoneNumber")
+ .build());
+ }
+}
diff --git a/demo/src/main/java/com/google/phonenumbers/demo/render/LibPhoneNumberRenderer.java b/demo/src/main/java/com/google/phonenumbers/demo/render/LibPhoneNumberRenderer.java
new file mode 100644
index 00000000..79591e42
--- /dev/null
+++ b/demo/src/main/java/com/google/phonenumbers/demo/render/LibPhoneNumberRenderer.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 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.
+ *
+ * @author Tobias Rogg
+ */
+
+package com.google.phonenumbers.demo.render;
+
+import com.google.template.soy.data.SoyTemplate;
+import com.google.template.soy.jbcsrc.api.SoySauce;
+import com.google.template.soy.jbcsrc.api.SoySauceBuilder;
+
+public abstract class LibPhoneNumberRenderer<T extends SoyTemplate> {
+
+ String render(T soyTemplate) {
+ SoySauce soySauce = new SoySauceBuilder().build();
+ return soySauce.newRenderer(soyTemplate).renderHtml().get().toString();
+ }
+
+ public abstract String genHtml();
+}
diff --git a/demo/src/main/java/com/google/phonenumbers/demo/render/ResultFileRenderer.java b/demo/src/main/java/com/google/phonenumbers/demo/render/ResultFileRenderer.java
new file mode 100644
index 00000000..37fe2dd6
--- /dev/null
+++ b/demo/src/main/java/com/google/phonenumbers/demo/render/ResultFileRenderer.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 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.
+ *
+ * @author Tobias Rogg
+ */
+
+package com.google.phonenumbers.demo.render;
+
+import com.google.i18n.phonenumbers.NumberParseException;
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
+import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
+import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
+import com.google.phonenumbers.demo.template.ResultFileTemplates.File;
+import java.util.StringTokenizer;
+
+public class ResultFileRenderer extends LibPhoneNumberRenderer<File> {
+ private final String defaultCountry;
+ private final String fileContents;
+ private final PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
+
+ public ResultFileRenderer(String defaultCountry, String fileContents) {
+ this.fileContents = fileContents;
+ this.defaultCountry = defaultCountry;
+ }
+
+ @Override
+ public String genHtml() {
+ File.Builder soyTemplate = File.builder();
+ int phoneNumberId = 0;
+ StringTokenizer tokenizer = new StringTokenizer(fileContents, ",");
+ while (tokenizer.hasMoreTokens()) {
+ String numberStr = tokenizer.nextToken();
+ phoneNumberId++;
+ try {
+ PhoneNumber number = phoneUtil.parseAndKeepRawInput(numberStr, defaultCountry);
+ boolean isNumberValid = phoneUtil.isValidNumber(number);
+ soyTemplate.addRows(
+ phoneNumberId,
+ numberStr,
+ isNumberValid ? phoneUtil.formatInOriginalFormat(number, defaultCountry) : "invalid",
+ isNumberValid ? phoneUtil.format(number, PhoneNumberFormat.INTERNATIONAL) : "invalid",
+ null);
+ } catch (NumberParseException e) {
+ soyTemplate.addRows(phoneNumberId, numberStr, null, null, e.toString());
+ }
+ }
+ return super.render(soyTemplate.build());
+ }
+}
diff --git a/demo/src/main/java/com/google/phonenumbers/demo/render/ResultRenderer.java b/demo/src/main/java/com/google/phonenumbers/demo/render/ResultRenderer.java
new file mode 100644
index 00000000..e491371a
--- /dev/null
+++ b/demo/src/main/java/com/google/phonenumbers/demo/render/ResultRenderer.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2022 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.
+ *
+ * @author Tobias Rogg
+ */
+
+package com.google.phonenumbers.demo.render;
+
+import com.google.common.collect.ImmutableList;
+import com.google.i18n.phonenumbers.AsYouTypeFormatter;
+import com.google.i18n.phonenumbers.PhoneNumberToCarrierMapper;
+import com.google.i18n.phonenumbers.PhoneNumberToTimeZonesMapper;
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
+import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
+import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
+import com.google.i18n.phonenumbers.ShortNumberInfo;
+import com.google.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
+import com.google.phonenumbers.demo.helper.WebHelper;
+import com.google.phonenumbers.demo.template.ResultTemplates.SingleNumber;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+public class ResultRenderer extends LibPhoneNumberRenderer<SingleNumber> {
+ private final String phoneNumber;
+ private final String defaultCountry;
+ private final Locale geocodingLocale;
+ private final PhoneNumber number;
+ private final PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
+ private final ShortNumberInfo shortInfo = ShortNumberInfo.getInstance();
+
+ public ResultRenderer(
+ String phoneNumber, String defaultCountry, Locale geocodingLocale, PhoneNumber number) {
+ this.phoneNumber = phoneNumber;
+ this.defaultCountry = defaultCountry;
+ this.geocodingLocale = geocodingLocale;
+ this.number = number;
+ }
+
+ @Override
+ public String genHtml() {
+ // Header info at Start of Page
+ SingleNumber.Builder soyTemplate =
+ SingleNumber.builder()
+ .setPhoneNumber(phoneNumber)
+ .setDefaultCountry(defaultCountry)
+ .setGeocodingLocale(geocodingLocale.toLanguageTag());
+
+ soyTemplate
+ .setCountryCode(number.getCountryCode())
+ .setNationalNumber(number.getNationalNumber())
+ .setExtension(number.getExtension())
+ .setCountryCodeSource(number.getCountryCodeSource().toString())
+ .setItalianLeadingZero(number.isItalianLeadingZero())
+ .setNumberOfLeadingZeros(number.getNumberOfLeadingZeros())
+ .setRawInput(number.getRawInput())
+ .setPreferredDomesticCarrierCode(number.getPreferredDomesticCarrierCode());
+
+ boolean isNumberValid = phoneUtil.isValidNumber(number);
+ boolean hasDefaultCountry = !defaultCountry.isEmpty() && !defaultCountry.equals("ZZ");
+
+ // Validation Results Table
+ soyTemplate
+ .setIsPossibleNumber(phoneUtil.isPossibleNumber(number))
+ .setIsValidNumber(isNumberValid)
+ .setIsValidNumberForRegion(
+ isNumberValid && hasDefaultCountry
+ ? phoneUtil.isValidNumberForRegion(number, defaultCountry)
+ : null)
+ .setPhoneNumberRegion(phoneUtil.getRegionCodeForNumber(number))
+ .setNumberType(phoneUtil.getNumberType(number).toString())
+ .setValidationResult(phoneUtil.isPossibleNumberWithReason(number).toString());
+
+ // Short Number Results Table
+ soyTemplate
+ .setIsPossibleShortNumber(shortInfo.isPossibleShortNumber(number))
+ .setIsValidShortNumber(shortInfo.isValidShortNumber(number))
+ .setIsPossibleShortNumberForRegion(
+ hasDefaultCountry
+ ? shortInfo.isPossibleShortNumberForRegion(number, defaultCountry)
+ : null)
+ .setIsValidShortNumberForRegion(
+ hasDefaultCountry
+ ? shortInfo.isValidShortNumberForRegion(number, defaultCountry)
+ : null);
+
+ // Formatting Results Table
+ soyTemplate
+ .setE164Format(isNumberValid ? phoneUtil.format(number, PhoneNumberFormat.E164) : "invalid")
+ .setOriginalFormat(phoneUtil.formatInOriginalFormat(number, defaultCountry))
+ .setNationalFormat(phoneUtil.format(number, PhoneNumberFormat.NATIONAL))
+ .setInternationalFormat(
+ isNumberValid ? phoneUtil.format(number, PhoneNumberFormat.INTERNATIONAL) : "invalid")
+ .setOutOfCountryFormatFromUs(
+ isNumberValid ? phoneUtil.formatOutOfCountryCallingNumber(number, "US") : "invalid")
+ .setOutOfCountryFormatFromCh(
+ isNumberValid ? phoneUtil.formatOutOfCountryCallingNumber(number, "CH") : "invalid")
+ .setMobileDiallingFormatFromUs(
+ isNumberValid ? phoneUtil.formatNumberForMobileDialing(number, "US", true) : "invalid")
+ .setNationalDiallingFormatWithPreferredCarrierCode(
+ isNumberValid ? phoneUtil.formatNationalNumberWithCarrierCode(number, "") : "invalid");
+
+ // Get As You Type Formatter Table
+ List<List<String>> rows = new ArrayList<>();
+ AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter(defaultCountry);
+ int rawNumberLength = phoneNumber.length();
+ for (int i = 0; i < rawNumberLength; i++) {
+ // Note this doesn't handle supplementary characters, but it shouldn't be a big deal as
+ // there are no dial-pad characters in the supplementary range.
+ char inputChar = phoneNumber.charAt(i);
+ rows.add(ImmutableList.of(String.valueOf(inputChar), formatter.inputDigit(inputChar)));
+ }
+ soyTemplate.setRows(rows);
+
+ // Geo Info Tables
+ String guidelinesLink = "https://github.com/google/libphonenumber/blob/master/CONTRIBUTING.md";
+ soyTemplate
+ .setDescriptionForNumber(
+ PhoneNumberOfflineGeocoder.getInstance()
+ .getDescriptionForNumber(number, geocodingLocale))
+ .setTimeZonesForNumber(
+ PhoneNumberToTimeZonesMapper.getInstance().getTimeZonesForNumber(number).toString())
+ .setNameForNumber(
+ PhoneNumberToCarrierMapper.getInstance().getNameForNumber(number, geocodingLocale))
+ .setNewIssueLink(WebHelper.getNewIssueLink(phoneNumber, defaultCountry, geocodingLocale))
+ .setGuidelinesLink(guidelinesLink);
+
+ return super.render(soyTemplate.build());
+ }
+}
diff --git a/demo/war/phonenumberparser.jsp b/demo/src/main/resources/com/google/phonenumbers/demo/input_form.soy
index 4e7b7f2f..cbaf91b0 100644
--- a/demo/war/phonenumberparser.jsp
+++ b/demo/src/main/resources/com/google/phonenumbers/demo/input_form.soy
@@ -1,15 +1,18 @@
-<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+{namespace com.google.phonenumbers.demo.inputForm}
+{template inputForm}
+{@param welcomeTitle: string}
<html>
<head>
<link type="text/css" rel="stylesheet" href="/stylesheets/main.css" />
+ <title>{$welcomeTitle}</title>
</head>
<body>
- <h2>Phone Number Parser Demo</h2>
+ <h2>{$welcomeTitle}</h2>
<form action="/phonenumberparser" method="post" accept-charset="UTF-8"
enctype="multipart/form-data">
<h2>Step 1</h2>
<p>
- Specify a Phone Number: <input type="text" name="phoneNumber" size="25">
+ Specify a Phone Number: <input type="text" name="phoneNumber" size="25" value="">
<p>
<b>Or</b> Upload a file containing phone numbers separated by comma.
<p>
@@ -37,3 +40,4 @@
</form>
</body>
</html>
+{/template}
diff --git a/demo/src/main/resources/com/google/phonenumbers/demo/result.soy b/demo/src/main/resources/com/google/phonenumbers/demo/result.soy
new file mode 100644
index 00000000..e3107109
--- /dev/null
+++ b/demo/src/main/resources/com/google/phonenumbers/demo/result.soy
@@ -0,0 +1,273 @@
+{namespace com.google.phonenumbers.demo.singleNumber}
+{template singleNumber}
+{@param phoneNumber: string}
+{@param defaultCountry: string}
+{@param geocodingLocale: string}
+
+{@param countryCode: int}
+{@param nationalNumber: int}
+{@param extension: string}
+{@param countryCodeSource: string}
+{@param italianLeadingZero: bool}
+{@param numberOfLeadingZeros: int}
+{@param rawInput: string}
+{@param preferredDomesticCarrierCode: string}
+
+{@param isPossibleNumber: bool}
+{@param isValidNumber: bool}
+{@param isValidNumberForRegion: bool|null}
+{@param phoneNumberRegion: string|null}
+{@param numberType: string}
+{@param validationResult: string}
+
+{@param isPossibleShortNumber: bool}
+{@param isValidShortNumber: bool}
+{@param isPossibleShortNumberForRegion: bool|null}
+{@param isValidShortNumberForRegion: bool|null}
+
+{@param e164Format: string}
+{@param originalFormat: string}
+{@param nationalFormat: string}
+{@param internationalFormat: string}
+{@param outOfCountryFormatFromUs: string}
+{@param outOfCountryFormatFromCh: string}
+{@param mobileDiallingFormatFromUs: string}
+{@param nationalDiallingFormatWithPreferredCarrierCode: string}
+
+{@param rows: list<list<string>>}
+
+{@param descriptionForNumber: string}
+{@param timeZonesForNumber: string}
+{@param nameForNumber: string}
+{@param newIssueLink: string}
+{@param guidelinesLink: string}
+
+
+<!DOCTYPE html>
+<HTML lang="en">
+<HEAD>
+ <LINK type="text/css" rel="stylesheet" href="/stylesheets/main.css"/>
+ <title>Results for {$phoneNumber}</title>
+</HEAD>
+<BODY>
+<p>Phone Number entered: {$phoneNumber}</p>
+<p>Default Country entered: {$defaultCountry}</p>
+<p>Language entered: {$geocodingLocale}</p>
+<DIV>
+ <TABLE border=1>
+ <TR>
+ <TD colspan=2>Parsing Result (parseAndKeepRawInput())</TD>
+ </TR>
+ <TR>
+ <TH>country_code</TH>
+ <TD>{$countryCode}</TD>
+ </TR>
+ <TR>
+ <TH>national_number</TH>
+ <TD>{$nationalNumber}</TD>
+ </TR>
+ <TR>
+ <TH>extension</TH>
+ <TD>{$extension}</TD>
+ </TR>
+ <TR>
+ <TH>country_code_source</TH>
+ <TD>{$countryCodeSource}</TD>
+ </TR>
+ <TR>
+ <TH>italian_leading_zero</TH>
+ <TD>{$italianLeadingZero}</TD>
+ </TR>
+ <TR>
+ <TH>number_of_leading_zeros</TH>
+ <TD>{$numberOfLeadingZeros}</TD>
+ </TR>
+ <TR>
+ <TH>raw_input</TH>
+ <TD>{$rawInput}</TD>
+ </TR>
+ <TR>
+ <TH>preferred_domestic_carrier_code</TH>
+ <TD>{$preferredDomesticCarrierCode}</TD>
+ </TR>
+ </TABLE>
+</DIV>
+<DIV>
+ <TABLE border=1>
+ <TR>
+ <TD colspan=2>Validation Results</TD>
+ </TR>
+ <TR>
+ <TH>Result from isPossibleNumber()</TH>
+ <TD>{$isPossibleNumber}</TD>
+ </TR>
+{if $isPossibleNumber}
+ {if $validationResult == "IS_POSSIBLE_LOCAL_ONLY"}
+ <TR>
+ <TH>Result from isPossibleNumberWithReason()</TH>
+ <TD>{$validationResult}</TD>
+ </TR>
+ <TR>
+ <TD colspan=2>Number is considered invalid as it is not a possible national number.</TD>
+ </TR>
+ {else}
+ <TR>
+ <TH>Result from isValidNumber()</TH>
+ <TD>{$isValidNumber}</TD>
+ </TR>
+ {if $isValidNumberForRegion != null}
+ <TR>
+ <TH>Result from isValidNumberForRegion()</TH>
+ <TD>{$isValidNumberForRegion}</TD>
+ </TR>
+ {/if}
+ <TR>
+ <TH>Phone Number region</TH>
+ <TD>{$phoneNumberRegion ?: ""}</TD>
+ </TR>
+ <TR>
+ <TH>Result from getNumberType()</TH>
+ <TD>{$numberType}</TD>
+ </TR>
+ {/if}
+{else}
+ <TR>
+ <TH>Result from isPossibleNumberWithReason()</TH>
+ <TD>{$validationResult}</TD>
+ </TR>
+ <TR>
+ <TD colspan=2>Note: Numbers that are not possible have type UNKNOWN, an unknown region, and are considered invalid.</TD>
+ </TR>
+{/if}
+ </TABLE>
+</DIV>
+
+{if not $isValidNumber}
+<DIV>
+ <TABLE border=1>
+ <TR><TD colspan=2>Short Number Results</TD></TR>
+ <TR>
+ <TH>Result from isPossibleShortNumber()</TH>
+ <TD>{$isPossibleShortNumber}</TD>
+ </TR>
+ {if $isPossibleShortNumber}
+ <TR>
+ <TH>Result from isValidShortNumber()</TH>
+ <TD>{$isValidShortNumber}</TD>
+ </TR>
+ {/if}
+ {if $isPossibleShortNumberForRegion != null}
+ <TR>
+ <TH>Result from isPossibleShortNumberForRegion()</TH>
+ <TD>{$isPossibleShortNumberForRegion}</TD>
+ </TR>
+ {if $isPossibleShortNumberForRegion and $isValidShortNumberForRegion != null}
+ <TR>
+ <TH>Result from isValidShortNumberForRegion()</TH>
+ <TD>{$isValidShortNumberForRegion}</TD>
+ </TR>
+ {/if}
+ {/if}
+ </TABLE>
+</DIV>
+{/if}
+
+<DIV>
+ <TABLE border=1>
+ <TR>
+ <TD colspan=2>Formatting Results</TD>
+ </TR>
+ <TR>
+ <TH>E164 format</TH>
+ <TD>{$e164Format}</TD>
+ </TR>
+ <TR>
+ <TH>Original format</TH>
+ <TD>{$originalFormat}</TD>
+ </TR>
+ <TR>
+ <TH>National format</TH>
+ <TD>{$nationalFormat}</TD>
+ </TR>
+ <TR>
+ <TH>International format</TH>
+ <TD>{$internationalFormat}</TD>
+ </TR>
+ <TR>
+ <TH>Out-of-country format from US</TH>
+ <TD>{$outOfCountryFormatFromUs}</TD>
+ </TR>
+ <TR>
+ <TH>Out-of-country format from CH</TH>
+ <TD>{$outOfCountryFormatFromCh}</TD>
+ </TR>
+ <TR>
+ <TH>Format for mobile dialing (calling from US)</TH>
+ <TD>{$mobileDiallingFormatFromUs}</TD>
+ </TR>
+ <TR>
+ <TH>Format for national dialing with preferred carrier code and empty fallback carrier code</TH>
+ <TD>{$nationalDiallingFormatWithPreferredCarrierCode}</TD>
+ </TR>
+ </TABLE>
+</DIV>
+
+<DIV>
+ <TABLE border=1>
+ <TR>
+ <TD colspan=2>AsYouTypeFormatter Results</TD>
+ </TR>
+ {for $row in $rows}
+ <TR>
+ <TH>Char entered: '{$row[0]}' Output: "</TH>
+ <TD>{$row[1]}</TD>
+ </TR>
+ {/for}
+
+ </TABLE>
+</DIV>
+
+
+{if $isValidNumber}
+
+<DIV>
+ <TABLE border=1>
+ <TR>
+ <TD colspan=2>PhoneNumberOfflineGeocoder Results</TD>
+ </TR>
+ <TR>
+ <TH>Location</TH>
+ <TD>{$descriptionForNumber}</TD>
+ </TR>
+ </TABLE>
+</DIV>
+<DIV>
+ <TABLE border=1>
+ <TR>
+ <TD colspan=2>PhoneNumberToTimeZonesMapper Results</TD>
+ </TR>
+ <TR>
+ <TH>Time zone(s)</TH>
+ <TD>{$timeZonesForNumber}</TD>
+ </TR>
+ </TABLE>
+</DIV>
+ {if ['MOBILE', 'FIXED_LINE_OR_MOBILE', 'PAGER'].contains($numberType)}
+<DIV>
+ <TABLE border=1>
+ <TR>
+ <TD colspan=2>PhoneNumberToCarrierMapper Results</TD>
+ </TR>
+ <TR>
+ <TH>Carrier</TH>
+ <TD>{$nameForNumber}</TD>
+ </TR>
+ </TABLE>
+</DIV>
+ {/if}
+{/if}
+
+<b style="color:red">File an issue</b>: by clicking on <a target="_blank" href="{$newIssueLink}">this link</a>, I confirm that I have read the <a target="_blank" href="{$guidelinesLink}">contributor's guidelines</a>.
+</BODY>
+</HTML>
+{/template} \ No newline at end of file
diff --git a/demo/src/main/resources/com/google/phonenumbers/demo/result_error.soy b/demo/src/main/resources/com/google/phonenumbers/demo/result_error.soy
new file mode 100644
index 00000000..1f3c4007
--- /dev/null
+++ b/demo/src/main/resources/com/google/phonenumbers/demo/result_error.soy
@@ -0,0 +1,20 @@
+{namespace com.google.phonenumbers.demo.error}
+{template error}
+{@param phoneNumber: string}
+{@param defaultCountry: string}
+{@param geocodingLocale: string}
+{@param error: string}
+<!DOCTYPE html>
+<HTML lang="en">
+<HEAD>
+ <LINK type="text/css" rel="stylesheet" href="/stylesheets/main.css"/>
+ <title>Results for {$phoneNumber}</title>
+</HEAD>
+<BODY>
+<p>Phone Number entered: {$phoneNumber}</p>
+<p>Default Country entered: {$defaultCountry}</p>
+<p>Language entered: {$geocodingLocale}</p>
+<p>An error occurred while parsing the input: {$error}
+</BODY>
+</HTML>
+{/template} \ No newline at end of file
diff --git a/demo/src/main/resources/com/google/phonenumbers/demo/result_file.soy b/demo/src/main/resources/com/google/phonenumbers/demo/result_file.soy
new file mode 100644
index 00000000..fd921fe4
--- /dev/null
+++ b/demo/src/main/resources/com/google/phonenumbers/demo/result_file.soy
@@ -0,0 +1,44 @@
+{namespace com.google.phonenumbers.demo.file}
+{template file}
+ {@param rows: list<[id: int, numberStr: string, prettyFormat: string|null, internationalFormat: string|null, error: string|null]>}
+
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <link rel="stylesheet" href="/stylesheets/main.css" />
+ <title>Results generated from phone numbers in the file provided</title>
+</head>
+<body>
+
+<h2>Results generated from phone numbers in the file provided:</h2>
+
+<table>
+ <tr>
+ <th>Id</th>
+ <th>Raw phone number</th>
+ <th>Pretty formatting</th>
+ <th>International format</th>
+ </tr>
+ {for $row in $rows}
+ <tr>
+ <td>{$row.id}</td>
+ <td>{$row.numberStr}</td>
+ {if $row.error == null}
+ <td>{$row.prettyFormat}</td>
+ <td>{$row.internationalFormat}</td>
+ {else}
+ <td colspan=2>{$row.error}</td>
+ {/if}
+ </tr>
+ {ifempty}
+ <tr>
+ <td colspan=4>No data in file detected!</td>
+ </tr>
+ {/for}
+
+
+</table>
+
+</body>
+</html>
+{/template} \ No newline at end of file
diff --git a/demo/war/WEB-INF/appengine-web.xml b/demo/src/main/webapp/WEB-INF/appengine-web.xml
index 1a367eca..1a367eca 100644
--- a/demo/war/WEB-INF/appengine-web.xml
+++ b/demo/src/main/webapp/WEB-INF/appengine-web.xml
diff --git a/demo/war/WEB-INF/logging.properties b/demo/src/main/webapp/WEB-INF/logging.properties
index a1720668..0c2ea51b 100644
--- a/demo/war/WEB-INF/logging.properties
+++ b/demo/src/main/webapp/WEB-INF/logging.properties
@@ -3,7 +3,7 @@
#
# To use this configuration, copy it into your application's WEB-INF
# folder and add the following to your appengine-web.xml:
-#
+#
# <system-properties>
# <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
# </system-properties>
diff --git a/demo/src/main/webapp/WEB-INF/web.xml b/demo/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 00000000..0b871087
--- /dev/null
+++ b/demo/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ version="2.5"
+ xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
+ http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+ <servlet>
+ <servlet-class>com.google.phonenumbers.demo.ResultServlet</servlet-class>
+ <servlet-name>PhoneNumberParser</servlet-name>
+ </servlet>
+ <servlet>
+ <servlet-class>com.google.phonenumbers.demo.InputServlet</servlet-class>
+ <servlet-name>Input</servlet-name>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>PhoneNumberParser</servlet-name>
+ <url-pattern>/phonenumberparser</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>Input</servlet-name>
+ <url-pattern>/inputform</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>Input</servlet-name>
+ <url-pattern>/</url-pattern>
+ </servlet-mapping>
+</web-app>
diff --git a/demo/war/favicon.ico b/demo/src/main/webapp/favicon.ico
index 789f24fc..789f24fc 100644
--- a/demo/war/favicon.ico
+++ b/demo/src/main/webapp/favicon.ico
Binary files differ
diff --git a/demo/war/stylesheets/main.css b/demo/src/main/webapp/stylesheets/main.css
index 80b8361a..80b8361a 100644
--- a/demo/war/stylesheets/main.css
+++ b/demo/src/main/webapp/stylesheets/main.css
diff --git a/demo/war/WEB-INF/lib/commons-fileupload-1.2.1.jar b/demo/war/WEB-INF/lib/commons-fileupload-1.2.1.jar
deleted file mode 100644
index aa209b38..00000000
--- a/demo/war/WEB-INF/lib/commons-fileupload-1.2.1.jar
+++ /dev/null
Binary files differ
diff --git a/demo/war/WEB-INF/lib/commons-io-1.4.jar b/demo/war/WEB-INF/lib/commons-io-1.4.jar
deleted file mode 100644
index 133dc6cb..00000000
--- a/demo/war/WEB-INF/lib/commons-io-1.4.jar
+++ /dev/null
Binary files differ
diff --git a/demo/war/WEB-INF/lib/commons-lang-2.6.jar b/demo/war/WEB-INF/lib/commons-lang-2.6.jar
deleted file mode 100644
index 98467d3a..00000000
--- a/demo/war/WEB-INF/lib/commons-lang-2.6.jar
+++ /dev/null
Binary files differ
diff --git a/demo/war/WEB-INF/lib/servlet-api-2.5.jar b/demo/war/WEB-INF/lib/servlet-api-2.5.jar
deleted file mode 100644
index fb524934..00000000
--- a/demo/war/WEB-INF/lib/servlet-api-2.5.jar
+++ /dev/null
Binary files differ
diff --git a/demo/war/WEB-INF/web.xml b/demo/war/WEB-INF/web.xml
deleted file mode 100644
index 5a4ba5c9..00000000
--- a/demo/war/WEB-INF/web.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns="http://java.sun.com/xml/ns/javaee"
- xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
- http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
- <servlet>
- <servlet-name>PhoneNumberParser</servlet-name>
- <servlet-class>com.google.phonenumbers.PhoneNumberParserServlet</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>PhoneNumberParser</servlet-name>
- <url-pattern>/phonenumberparser</url-pattern>
- </servlet-mapping>
- <welcome-file-list>
- <welcome-file>phonenumberparser.jsp</welcome-file>
- </welcome-file-list>
-</web-app>