data/libphonenumber/java/demo/src/com/google/phonenumbers/PhoneNumberParserServlet.java in phonelib-0.5.4 vs data/libphonenumber/java/demo/src/com/google/phonenumbers/PhoneNumberParserServlet.java in phonelib-0.5.5

- old
+ new

@@ -16,29 +16,36 @@ * @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.Phonenumber.PhoneNumber; +import com.google.i18n.phonenumbers.ShortNumberInfo; import com.google.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder; 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; 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; @@ -51,10 +58,11 @@ * 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 = ""; @@ -67,11 +75,11 @@ 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"); + 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) { @@ -91,35 +99,48 @@ } catch (FileUploadException e1) { e1.printStackTrace(); } StringBuilder output; + resp.setContentType("text/html"); + resp.setCharacterEncoding(UTF_8.name()); if (fileContents.length() == 0) { - output = getOutputForSingleNumber(phoneNumber, defaultCountry, languageCode, regionCode); - resp.setContentType("text/html"); - resp.setCharacterEncoding("UTF-8"); - resp.getWriter().println("<html><head>"); - resp.getWriter().println( - "<link type=\"text/css\" rel=\"stylesheet\" href=\"/stylesheets/main.css\" />"); - resp.getWriter().println("</head>"); - resp.getWriter().println("<body>"); - resp.getWriter().println("Phone Number entered: " + phoneNumber + "<br>"); - resp.getWriter().println("defaultCountry entered: " + defaultCountry + "<br>"); - resp.getWriter().println( - "Language entered: " + languageCode + - (regionCode.length() == 0 ? "" : " (" + regionCode + ")" + "<br>")); + // 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 { - output = getOutputForFile(defaultCountry, fileContents); - resp.setContentType("text/html"); + resp.getWriter().println(getOutputForFile(defaultCountry, fileContents)); } - resp.getWriter().println(output); - resp.getWriter().println("</body></html>"); } + /** + * 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.getWriter().println( + getOutputForSingleNumber(phoneNumber, defaultCountry, geocodingLocale)); + } + private StringBuilder getOutputForFile(String defaultCountry, String fileContents) { - StringBuilder output = new StringBuilder(); - output.append("<HTML><HEAD><TITLE>Results generated from phone numbers in the file provided:" + 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>"); @@ -130,25 +151,29 @@ 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(numberStr).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(prettyFormat).append(" </TD> \n"); - output.append("<TD align=center>").append(internationalFormat).append(" </TD> \n"); + 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(e.toString()).append(" </TD> \n"); + output.append("<TD align=center colspan=2>").append( + StringEscapeUtils.escapeHtml(e.toString())).append(" </TD> \n"); } output.append("</TR>"); } output.append("</BODY></HTML>"); return output; @@ -160,17 +185,92 @@ output.append("<TD>").append(data.length() > 0 ? data : "&nbsp;").append("</TD>"); output.append("</TR>"); } /** - * The defaultCountry here is used for parsing phoneNumber. The languageCode and regionCode are - * used to specify the language used for displaying the area descriptions generated from phone - * number geocoding. + * 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, UTF_8.name())); + if (!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. + */ + private String getNewIssueLink( + String phoneNumber, String defaultCountry, Locale geocodingLocale) { + boolean hasDefaultCountry = !defaultCountry.isEmpty() && defaultCountry != "ZZ"; + String issueTitle = "Validation issue with " + phoneNumber + + (hasDefaultCountry ? " (" + defaultCountry + ")" : ""); + + // Issue template. This must be kept in sync with the template in + // https://github.com/googlei18n/libphonenumber/blob/master/CONTRIBUTING.md. + StringBuilder issueTemplate = new StringBuilder( + "Please read the \"guidelines for contributing\" (linked above) and fill " + + "in the template below.\n\n"); + issueTemplate.append("Country/region affected (e.g., \"US\"): ") + .append(defaultCountry).append("\n\n"); + issueTemplate.append("Example number(s) affected (\"+1 555 555-1234\"): ") + .append(phoneNumber).append("\n\n"); + issueTemplate.append( + "The phone number range(s) to which the issue applies (\"+1 555 555-XXXX\"): \n\n"); + issueTemplate.append( + "The type of the number(s) (\"fixed-line\", \"mobile\", \"short code\", etc.): \n\n"); + issueTemplate.append( + "The cost, if applicable (\"toll-free\", \"premium rate\", \"shared cost\"): \n\n"); + issueTemplate.append( + "Supporting evidence (for example, national numbering plan, announcement from mobile " + + "carrier, news article): **IMPORTANT - anything posted here is made public. " + + "Read the guidelines first!** \n\n"); + issueTemplate.append("[link to demo](" + + getPermaLinkURL(phoneNumber, defaultCountry, geocodingLocale, true /* absoluteURL */) + + ")\n\n"); + String newIssueLink = "https://github.com/googlei18n/libphonenumber/issues/new?title="; + try { + newIssueLink += URLEncoder.encode(issueTitle, UTF_8.name()) + "&body=" + + URLEncoder.encode(issueTemplate.toString(), 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, String languageCode, String regionCode) { - StringBuilder output = new StringBuilder(); + 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</TD></TR>"); @@ -185,10 +285,11 @@ 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); @@ -198,11 +299,11 @@ output.append("<TR><TD colspan=2>Note: numbers that are not possible have type " + "UNKNOWN, an unknown region, and are considered invalid.</TD></TR>"); } else { appendLine("Result from isValidNumber()", Boolean.toString(isNumberValid), output); if (isNumberValid) { - if (!defaultCountry.isEmpty() && defaultCountry != "ZZ") { + if (hasDefaultCountry) { appendLine( "Result from isValidNumberForRegion()", Boolean.toString(phoneUtil.isValidNumberForRegion(number, defaultCountry)), output); } @@ -212,10 +313,36 @@ appendLine("Result from getNumberType()", numberType.toString(), output); } 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", @@ -258,11 +385,11 @@ output.append("<TABLE border=1>"); output.append("<TR><TD colspan=2>PhoneNumberOfflineGeocoder Results</TD></TR>"); appendLine( "Location", PhoneNumberOfflineGeocoder.getInstance().getDescriptionForNumber( - number, new Locale(languageCode, regionCode)), + number, geocodingLocale), output); output.append("</TABLE>"); output.append("</DIV>"); output.append("<DIV>"); @@ -281,18 +408,26 @@ output.append("<DIV>"); output.append("<TABLE border=1>"); output.append("<TR><TD colspan=2>PhoneNumberToCarrierMapper Results</TD></TR>"); appendLine( "Carrier", - PhoneNumberToCarrierMapper.getInstance().getNameForNumber( - number, new Locale(languageCode, regionCode)), + PhoneNumberToCarrierMapper.getInstance().getNameForNumber(number, geocodingLocale), output); output.append("</TABLE>"); output.append("</DIV>"); } } + + String newIssueLink = getNewIssueLink(phoneNumber, defaultCountry, geocodingLocale); + String guidelinesLink = + "https://github.com/googlei18n/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(e.toString()); + output.append(StringEscapeUtils.escapeHtml(e.toString())); } + output.append("</BODY></HTML>"); return output; } }