diff options
Diffstat (limited to 'demo')
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 Binary files differnew file mode 100644 index 00000000..4c21985e --- /dev/null +++ b/demo/.readme_res/App_Engine_Run_Config.png 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 : " ").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 Binary files differindex 789f24fc..789f24fc 100644 --- a/demo/war/favicon.ico +++ b/demo/src/main/webapp/favicon.ico 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 Binary files differdeleted file mode 100644 index aa209b38..00000000 --- a/demo/war/WEB-INF/lib/commons-fileupload-1.2.1.jar +++ /dev/null diff --git a/demo/war/WEB-INF/lib/commons-io-1.4.jar b/demo/war/WEB-INF/lib/commons-io-1.4.jar Binary files differdeleted file mode 100644 index 133dc6cb..00000000 --- a/demo/war/WEB-INF/lib/commons-io-1.4.jar +++ /dev/null diff --git a/demo/war/WEB-INF/lib/commons-lang-2.6.jar b/demo/war/WEB-INF/lib/commons-lang-2.6.jar Binary files differdeleted file mode 100644 index 98467d3a..00000000 --- a/demo/war/WEB-INF/lib/commons-lang-2.6.jar +++ /dev/null diff --git a/demo/war/WEB-INF/lib/servlet-api-2.5.jar b/demo/war/WEB-INF/lib/servlet-api-2.5.jar Binary files differdeleted file mode 100644 index fb524934..00000000 --- a/demo/war/WEB-INF/lib/servlet-api-2.5.jar +++ /dev/null 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> |