Home | History | Annotate | Download | only in phonenumbers
      1 /*
      2  * Copyright (C) 2011 The Libphonenumber Authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  *
     16  * @author Shaopeng Jia
     17  */
     18 
     19 package com.google.phonenumbers;
     20 
     21 import com.google.i18n.phonenumbers.AsYouTypeFormatter;
     22 import com.google.i18n.phonenumbers.NumberParseException;
     23 import com.google.i18n.phonenumbers.PhoneNumberToCarrierMapper;
     24 import com.google.i18n.phonenumbers.PhoneNumberToTimeZonesMapper;
     25 import com.google.i18n.phonenumbers.PhoneNumberUtil;
     26 import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
     27 import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberType;
     28 import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
     29 import com.google.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
     30 
     31 import org.apache.commons.fileupload.FileItemIterator;
     32 import org.apache.commons.fileupload.FileItemStream;
     33 import org.apache.commons.fileupload.FileUploadException;
     34 import org.apache.commons.fileupload.servlet.ServletFileUpload;
     35 import org.apache.commons.fileupload.util.Streams;
     36 import org.apache.commons.io.IOUtils;
     37 
     38 import java.io.IOException;
     39 import java.io.InputStream;
     40 import java.util.Locale;
     41 import java.util.StringTokenizer;
     42 
     43 import javax.servlet.http.HttpServlet;
     44 import javax.servlet.http.HttpServletRequest;
     45 import javax.servlet.http.HttpServletResponse;
     46 
     47 /**
     48  * A servlet that accepts requests that contain strings representing a phone number and a default
     49  * country, and responds with results from parsing, validating and formatting the number. The
     50  * default country is a two-letter region code representing the country that we are expecting the
     51  * number to be from.
     52  */
     53 @SuppressWarnings("serial")
     54 public class PhoneNumberParserServlet extends HttpServlet {
     55   private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
     56   public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
     57     String phoneNumber = null;
     58     String defaultCountry = null;
     59     String languageCode = "en";  // Default languageCode to English if nothing is entered.
     60     String regionCode = "";
     61     String fileContents = null;
     62     ServletFileUpload upload = new ServletFileUpload();
     63     upload.setSizeMax(50000);
     64     try {
     65       FileItemIterator iterator = upload.getItemIterator(req);
     66       while (iterator.hasNext()) {
     67         FileItemStream item = iterator.next();
     68         InputStream in = item.openStream();
     69         if (item.isFormField()) {
     70           String fieldName = item.getFieldName();
     71           if (fieldName.equals("phoneNumber")) {
     72             phoneNumber = Streams.asString(in, "UTF-8");
     73           } else if (fieldName.equals("defaultCountry")) {
     74             defaultCountry = Streams.asString(in).toUpperCase();
     75           } else if (fieldName.equals("languageCode")) {
     76             String languageEntered = Streams.asString(in).toLowerCase();
     77             if (languageEntered.length() > 0) {
     78               languageCode = languageEntered;
     79             }
     80           } else if (fieldName.equals("regionCode")) {
     81             regionCode = Streams.asString(in).toUpperCase();
     82           }
     83         } else {
     84           try {
     85             fileContents = IOUtils.toString(in);
     86           } finally {
     87             IOUtils.closeQuietly(in);
     88           }
     89         }
     90       }
     91     } catch (FileUploadException e1) {
     92       e1.printStackTrace();
     93     }
     94 
     95     StringBuilder output;
     96     if (fileContents.length() == 0) {
     97       output = getOutputForSingleNumber(phoneNumber, defaultCountry, languageCode, regionCode);
     98       resp.setContentType("text/html");
     99       resp.setCharacterEncoding("UTF-8");
    100       resp.getWriter().println("<html><head>");
    101       resp.getWriter().println(
    102           "<link type=\"text/css\" rel=\"stylesheet\" href=\"/stylesheets/main.css\" />");
    103       resp.getWriter().println("</head>");
    104       resp.getWriter().println("<body>");
    105       resp.getWriter().println("Phone Number entered: " + phoneNumber + "<br>");
    106       resp.getWriter().println("defaultCountry entered: " + defaultCountry + "<br>");
    107       resp.getWriter().println(
    108           "Language entered: " + languageCode +
    109               (regionCode.length() == 0 ? "" : " (" + regionCode + ")" + "<br>"));
    110     } else {
    111       output = getOutputForFile(defaultCountry, fileContents);
    112       resp.setContentType("text/html");
    113     }
    114     resp.getWriter().println(output);
    115     resp.getWriter().println("</body></html>");
    116   }
    117 
    118   private StringBuilder getOutputForFile(String defaultCountry, String fileContents) {
    119     StringBuilder output = new StringBuilder();
    120     output.append("<HTML><HEAD><TITLE>Results generated from phone numbers in the file provided:"
    121         + "</TITLE></HEAD><BODY>");
    122     output.append("<TABLE align=center border=1>");
    123     output.append("<TH align=center>ID</TH>");
    124     output.append("<TH align=center>Raw phone number</TH>");
    125     output.append("<TH align=center>Pretty formatting</TH>");
    126     output.append("<TH align=center>International format</TH>");
    127 
    128     int phoneNumberId = 0;
    129     StringTokenizer tokenizer = new StringTokenizer(fileContents, ",");
    130     while (tokenizer.hasMoreTokens()) {
    131       String numberStr = tokenizer.nextToken();
    132       phoneNumberId++;
    133       output.append("<TR>");
    134       output.append("<TD align=center>").append(phoneNumberId).append(" </TD> \n");
    135       output.append("<TD align=center>").append(numberStr).append(" </TD> \n");
    136       try {
    137         PhoneNumber number = phoneUtil.parseAndKeepRawInput(numberStr, defaultCountry);
    138         boolean isNumberValid = phoneUtil.isValidNumber(number);
    139         String prettyFormat = isNumberValid
    140             ? phoneUtil.formatInOriginalFormat(number, defaultCountry)
    141             : "invalid";
    142         String internationalFormat = isNumberValid
    143             ? phoneUtil.format(number, PhoneNumberFormat.INTERNATIONAL)
    144             : "invalid";
    145 
    146         output.append("<TD align=center>").append(prettyFormat).append(" </TD> \n");
    147         output.append("<TD align=center>").append(internationalFormat).append(" </TD> \n");
    148       } catch (NumberParseException e) {
    149         output.append("<TD align=center colspan=2>").append(e.toString()).append(" </TD> \n");
    150       }
    151       output.append("</TR>");
    152     }
    153     output.append("</BODY></HTML>");
    154     return output;
    155   }
    156 
    157   private void appendLine(String title, String data, StringBuilder output) {
    158     output.append("<TR>");
    159     output.append("<TH>").append(title).append("</TH>");
    160     output.append("<TD>").append(data.length() > 0 ? data : "&nbsp;").append("</TD>");
    161     output.append("</TR>");
    162   }
    163 
    164   /**
    165    * The defaultCountry here is used for parsing phoneNumber. The languageCode and regionCode are
    166    * used to specify the language used for displaying the area descriptions generated from phone
    167    * number geocoding.
    168    */
    169   private StringBuilder getOutputForSingleNumber(
    170       String phoneNumber, String defaultCountry, String languageCode, String regionCode) {
    171     StringBuilder output = new StringBuilder();
    172     try {
    173       PhoneNumber number = phoneUtil.parseAndKeepRawInput(phoneNumber, defaultCountry);
    174       output.append("<DIV>");
    175       output.append("<TABLE border=1>");
    176       output.append("<TR><TD colspan=2>Parsing Result</TD></TR>");
    177 
    178       appendLine("country_code", Integer.toString(number.getCountryCode()), output);
    179       appendLine("national_number", Long.toString(number.getNationalNumber()), output);
    180       appendLine("extension", number.getExtension(), output);
    181       appendLine("country_code_source", number.getCountryCodeSource().toString(), output);
    182       appendLine("italian_leading_zero", Boolean.toString(number.isItalianLeadingZero()), output);
    183       appendLine("raw_input", number.getRawInput(), output);
    184       output.append("</TABLE>");
    185       output.append("</DIV>");
    186 
    187       boolean isPossible = phoneUtil.isPossibleNumber(number);
    188       boolean isNumberValid = phoneUtil.isValidNumber(number);
    189       PhoneNumberType numberType = phoneUtil.getNumberType(number);
    190 
    191       output.append("<DIV>");
    192       output.append("<TABLE border=1>");
    193       output.append("<TR><TD colspan=2>Validation Results</TD></TR>");
    194       appendLine("Result from isPossibleNumber()", Boolean.toString(isPossible), output);
    195       if (!isPossible) {
    196         appendLine("Result from isPossibleNumberWithReason()",
    197                    phoneUtil.isPossibleNumberWithReason(number).toString(), output);
    198         output.append("<TR><TD colspan=2>Note: numbers that are not possible have type " +
    199                       "UNKNOWN, an unknown region, and are considered invalid.</TD></TR>");
    200       } else {
    201         appendLine("Result from isValidNumber()", Boolean.toString(isNumberValid), output);
    202         if (isNumberValid) {
    203           if (!defaultCountry.isEmpty() && defaultCountry != "ZZ") {
    204             appendLine(
    205                 "Result from isValidNumberForRegion()",
    206                 Boolean.toString(phoneUtil.isValidNumberForRegion(number, defaultCountry)),
    207                 output);
    208           }
    209         }
    210         String region = phoneUtil.getRegionCodeForNumber(number);
    211         appendLine("Phone Number region", region == null ? "" : region, output);
    212         appendLine("Result from getNumberType()", numberType.toString(), output);
    213       }
    214       output.append("</TABLE>");
    215       output.append("</DIV>");
    216 
    217       output.append("<DIV>");
    218       output.append("<TABLE border=1>");
    219       output.append("<TR><TD colspan=2>Formatting Results</TD></TR>");
    220       appendLine("E164 format",
    221                  isNumberValid ? phoneUtil.format(number, PhoneNumberFormat.E164) : "invalid",
    222                  output);
    223       appendLine("Original format",
    224                  phoneUtil.formatInOriginalFormat(number, defaultCountry), output);
    225       appendLine("National format", phoneUtil.format(number, PhoneNumberFormat.NATIONAL), output);
    226       appendLine(
    227           "International format",
    228           isNumberValid ? phoneUtil.format(number, PhoneNumberFormat.INTERNATIONAL) : "invalid",
    229           output);
    230       appendLine(
    231           "Out-of-country format from US",
    232           isNumberValid ? phoneUtil.formatOutOfCountryCallingNumber(number, "US") : "invalid",
    233           output);
    234       appendLine(
    235           "Out-of-country format from CH",
    236           isNumberValid ? phoneUtil.formatOutOfCountryCallingNumber(number, "CH") : "invalid",
    237           output);
    238       output.append("</TABLE>");
    239       output.append("</DIV>");
    240 
    241       AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter(defaultCountry);
    242       int rawNumberLength = phoneNumber.length();
    243       output.append("<DIV>");
    244       output.append("<TABLE border=1>");
    245       output.append("<TR><TD colspan=2>AsYouTypeFormatter Results</TD></TR>");
    246       for (int i = 0; i < rawNumberLength; i++) {
    247         // Note this doesn't handle supplementary characters, but it shouldn't be a big deal as
    248         // there are no dial-pad characters in the supplementary range.
    249         char inputChar = phoneNumber.charAt(i);
    250         appendLine("Char entered: '" + inputChar + "' Output: ",
    251                    formatter.inputDigit(inputChar), output);
    252       }
    253       output.append("</TABLE>");
    254       output.append("</DIV>");
    255 
    256       if (isNumberValid) {
    257         output.append("<DIV>");
    258         output.append("<TABLE border=1>");
    259         output.append("<TR><TD colspan=2>PhoneNumberOfflineGeocoder Results</TD></TR>");
    260         appendLine(
    261             "Location",
    262             PhoneNumberOfflineGeocoder.getInstance().getDescriptionForNumber(
    263                 number, new Locale(languageCode, regionCode)),
    264             output);
    265         output.append("</TABLE>");
    266         output.append("</DIV>");
    267 
    268         output.append("<DIV>");
    269         output.append("<TABLE border=1>");
    270         output.append("<TR><TD colspan=2>PhoneNumberToTimeZonesMapper Results</TD></TR>");
    271         appendLine(
    272             "Time zone(s)",
    273             PhoneNumberToTimeZonesMapper.getInstance().getTimeZonesForNumber(number).toString(),
    274             output);
    275         output.append("</TABLE>");
    276         output.append("</DIV>");
    277 
    278         if (numberType == PhoneNumberType.MOBILE ||
    279             numberType == PhoneNumberType.FIXED_LINE_OR_MOBILE ||
    280             numberType == PhoneNumberType.PAGER) {
    281           output.append("<DIV>");
    282           output.append("<TABLE border=1>");
    283           output.append("<TR><TD colspan=2>PhoneNumberToCarrierMapper Results</TD></TR>");
    284           appendLine(
    285               "Carrier",
    286               PhoneNumberToCarrierMapper.getInstance().getNameForNumber(
    287                   number, new Locale(languageCode, regionCode)),
    288               output);
    289           output.append("</TABLE>");
    290           output.append("</DIV>");
    291         }
    292       }
    293     } catch (NumberParseException e) {
    294       output.append(e.toString());
    295     }
    296     return output;
    297   }
    298 }
    299