Java tutorial
/* * #%L * Netarchivesuite - common * %% * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University Library, * the National Library of France and the Austrian National Library. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * <http://www.gnu.org/licenses/lgpl-2.1.html>. * #L% */ package dk.netarkivet.common.webinterface; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.text.NumberFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.PageContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import dk.netarkivet.common.CommonSettings; import dk.netarkivet.common.Constants; import dk.netarkivet.common.exceptions.ArgumentNotValid; import dk.netarkivet.common.exceptions.ForwardedToErrorPage; import dk.netarkivet.common.exceptions.IOFailure; import dk.netarkivet.common.utils.I18n; import dk.netarkivet.common.utils.Settings; import dk.netarkivet.common.utils.StringTree; /** * This is a utility class containing methods for use in the GUI for netarkivet. */ public class HTMLUtils { /** Web page title placeholder. */ private static String TITLE_PLACEHOLDER = "STRING_1"; /** External JavaScript files placeholder. */ private static String JS_PLACEHOLDER = "JS_TO_INCLUDE"; private static String WEBPAGE_HEADER_TEMPLATE_TOP = "<!DOCTYPE html " + "PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \n " + " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" + "<html xmlns=\"http://www.w3.org/1999/xhtml\"" + " xml:lang=\"en\" lang=\"en\">\n" + "<head>\n" + "<meta content=\"text/html; charset=UTF-8\" " + "http-equiv= \"content-type\" />" + "<meta http-equiv=\"Expires\" content=\"0\"/>\n" + "<meta http-equiv=\"Cache-Control\" content=\"no-cache\"/>\n" + "<meta http-equiv=\"Pragma\" content=\"no-cache\"/> \n"; private static String WEBPAGE_HEADER_AUTOREFRESH = "<meta http-equiv=\"refresh\" content=\"" + TITLE_PLACEHOLDER + "\"/> \n"; private static String WEBPAGE_HEADER_TEMPLATE_BOTTOM = "<title>" + TITLE_PLACEHOLDER + "</title>\n" + "<script type=\"text/javascript\">\n" + "<!--\n" + "function giveFocus() {\n" + " var e = document.getElementById('focusElement');\n" + " if (e != null) {\n" + " var elms = e.getElementsByTagName('*');\n" + " if (elms != null && elms.length != null " + " && elms.item != null && elms.length > 0) {\n" + " var e2 = elms.item(0);\n" + " if (e2 != null && e2.focus != null) {\n" + " }\n" + " e2.focus();\n" + " }\n" + " }\n" + "}\n" + "-->\n" + "</script>\n" + JS_PLACEHOLDER + "\n" + "<link rel=\"stylesheet\" href=\"./netarkivet.css\" " + "type=\"text/css\" />\n" + "<link rel=\"stylesheet\" type=\"text/css\" media=\"all\" " + "href=\"./jscalendar/calendar-win2k-cold-1.css\" " + "title=\"./jscalendar/win2k-cold-1\" />\n" + "</head> <body onload=\"giveFocus()\">\n"; /** Logger for this class. */ private static Log log = LogFactory.getLog(HTMLUtils.class.getName()); /** Translations for this module. */ private static final I18n I18N = new I18n(Constants.TRANSLATIONS_BUNDLE); /** * Private constructor. There is no reason to instantiate this class. */ private HTMLUtils() { // Nothing to initialize } /** * Url encodes a string in UTF-8. This encodes _all_ non-letter non-number characters except '-', '_' and '.'. The * characters '/' and ':' are encoded. * * @param s the string to encode * @return the encoded string */ public static String encode(String s) { ArgumentNotValid.checkNotNull(s, "s"); try { return URLEncoder.encode(s, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new ArgumentNotValid(URLEncoder.class.getName() + " does not support UTF-8", e); } } /** * Url decodes a string encoded in UTF-8. * * @param s the string to decode * @return the decoded string */ public static String decode(String s) { ArgumentNotValid.checkNotNull(s, "s"); try { return URLDecoder.decode(s, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new ArgumentNotValid(URLDecoder.class.getName() + " does not support UTF-8", e); } } /** * Prints the header information for the webpages in the GUI. This includes the navigation menu, and links for * changing the language. The title of the page is generated internationalised from sitesections. If you want to * specify it, use the overloaded method. * * @param context The context of the web page request. * @throws IOException if an error occurs during writing of output. */ public static void generateHeader(PageContext context) throws IOException { ArgumentNotValid.checkNotNull(context, "context"); String url = ((HttpServletRequest) context.getRequest()).getRequestURL().toString(); Locale locale = context.getResponse().getLocale(); String title = getTitle(url, locale); generateHeader(title, context); } /** * Prints the header information for the webpages in the GUI. This includes the navigation menu, and links for * changing the language. The title of the page is generated internationalised from sitesections. If you want to * specify it, use the overloaded method. * * @param context The context of the web page request. * @throws IOException if an error occurs during writing of output. */ public static void generateHeader(PageContext context, String... jsToInclude) throws IOException { ArgumentNotValid.checkNotNull(context, "context"); String url = ((HttpServletRequest) context.getRequest()).getRequestURL().toString(); Locale locale = context.getResponse().getLocale(); String title = getTitle(url, locale); generateHeader(title, context, jsToInclude); } /** * Prints the header information for the webpages in the GUI. This includes the navigation menu, and links for * changing the language. The title of the page is generated internationalised from sitesections. If you want to * specify it, use the overloaded method. * * @param context The context of the web page request. * @param refreshInSeconds auto-refresh time in seconds * @throws IOException if an error occurs during writing of output. */ public static void generateHeader(PageContext context, long refreshInSeconds) throws IOException { ArgumentNotValid.checkNotNull(context, "context"); String url = ((HttpServletRequest) context.getRequest()).getRequestURL().toString(); Locale locale = context.getResponse().getLocale(); String title = getTitle(url, locale); generateHeader(title, refreshInSeconds, context); } /** * Prints the header information for the webpages in the GUI. This includes the navigation menu, and links for * changing the language. * * @param title An internationalised title of the page. * @param context The context of the web page request. * @param jsToInclude path(s) to external .js files to include in header. * @throws IOException if an error occurs during writing to output. */ public static void generateHeader(String title, PageContext context, String... jsToInclude) throws IOException { ArgumentNotValid.checkNotNull(title, "title"); ArgumentNotValid.checkNotNull(context, "context"); JspWriter out = context.getOut(); String url = ((HttpServletRequest) context.getRequest()).getRequestURL().toString(); Locale locale = context.getResponse().getLocale(); title = escapeHtmlValues(title); log.debug("Loaded URL '" + url + "' with title '" + title + "'"); out.print(WEBPAGE_HEADER_TEMPLATE_TOP); String includeJs = ""; if (jsToInclude != null && jsToInclude.length > 0) { for (String js : jsToInclude) { includeJs += "<script type=\"text/javascript\" src=\"" + js + "\"></script>\n"; } } out.print(WEBPAGE_HEADER_TEMPLATE_BOTTOM.replace(TITLE_PLACEHOLDER, title).replace(JS_PLACEHOLDER, includeJs)); // Start the two column / one row table which fills the page out.print("<table id =\"main_table\"><tr>\n"); // fill in data in the left column generateNavigationTree(out, url, locale); // The right column contains the active form content for this page out.print("<td valign = \"top\" >\n"); // Language links generateLanguageLinks(out); } /** * Prints the header information for the webpages in the GUI. This includes the navigation menu, and links for * changing the language. * * @param title An internationalised title of the page. * @param context The context of the web page request. * @param refreshInSeconds auto-refresh time in seconds * @throws IOException if an error occurs during writing to output. */ public static void generateHeader(String title, long refreshInSeconds, PageContext context) throws IOException { ArgumentNotValid.checkNotNull(title, "title"); ArgumentNotValid.checkNotNull(context, "context"); JspWriter out = context.getOut(); String url = ((HttpServletRequest) context.getRequest()).getRequestURL().toString(); Locale locale = context.getResponse().getLocale(); title = escapeHtmlValues(title); log.debug("Loaded URL '" + url + "' with title '" + title + "'"); out.print(WEBPAGE_HEADER_TEMPLATE_TOP); if (refreshInSeconds > 0) { out.print(WEBPAGE_HEADER_AUTOREFRESH.replace(TITLE_PLACEHOLDER, Long.toString(refreshInSeconds))); } out.print(WEBPAGE_HEADER_TEMPLATE_BOTTOM.replace(TITLE_PLACEHOLDER, title).replace(JS_PLACEHOLDER, "")); // Start the two column / one row table which fills the page out.print("<table id =\"main_table\"><tr>\n"); // fill in data in the left column generateNavigationTree(out, url, locale); // The right column contains the active form content for this page out.print("<td valign = \"top\" >\n"); // Language links generateLanguageLinks(out); } /** * Get the locale according to header context information. * * @param context The context of the web page request. * @return The locale given in the the page response. */ public static Locale getLocaleObject(PageContext context) { ArgumentNotValid.checkNotNull(context, "context"); return context.getResponse().getLocale(); } /** * Prints out links to change languages. Will read locales and names of languages from settings, and write them as * links to the page "lang.jsp". The locale will be given to this page as a parameter, the name will be shown as the * text of the link * * @param out the writer to which the links are written. * @throws IOException if an error occurs during writing of output. */ private static void generateLanguageLinks(JspWriter out) throws IOException { out.print("<div class=\"languagelinks\">"); StringTree<String> webinterfaceSettings = Settings.getTree(CommonSettings.WEBINTERFACE_SETTINGS); for (StringTree<String> language : webinterfaceSettings.getSubTrees(CommonSettings.WEBINTERFACE_LANGUAGE)) { out.print(String.format("<a href=\"lang.jsp?locale=%s&name=%s\">%s</a> ", escapeHtmlValues(encode(language.getValue(CommonSettings.WEBINTERFACE_LANGUAGE_LOCALE))), escapeHtmlValues(encode(language.getValue(CommonSettings.WEBINTERFACE_LANGUAGE_NAME))), escapeHtmlValues(language.getValue(CommonSettings.WEBINTERFACE_LANGUAGE_NAME)))); } out.print("</div>"); } /** * Prints out the navigation tree appearing as a <td>in the left column of the "main_table" table. Subpages are * shown only for the currently-active main-heading of the sections defined in settings. * * @param out the writer to which the output must be written. * @param url the url of the page. * @param locale The locale selecting the language. * @throws IOException if the output cannot be written. */ private static void generateNavigationTree(JspWriter out, String url, Locale locale) throws IOException { out.print("<td valign=\"top\" id=\"menu\">\n"); // The list of menu items is presented as a 1-column table out.print("<table id=\"menu_table\">\n"); String s = I18N.getString(locale, "sidebar.title.menu"); out.print("<tr><td><a class=\"sidebarHeader\" href=\"index.jsp\">" + "<img src=\"transparent_menu_logo.png\" alt=\"" + s + "\"/> " + s + "</a></td></tr>\n"); for (SiteSection section : SiteSection.getSections()) { section.generateNavigationTree(out, url, locale); } out.print("</table>\n"); out.print("</td>\n"); } /** * Writes out footer information to close the page. * * @param out the writer to which the information is written * @throws IOException if the output cannot be written */ public static void generateFooter(JspWriter out) throws IOException { ArgumentNotValid.checkNotNull(out, "out"); // Close the element containing the page content out.print("</td>\n"); // Close the single row in the table out.print("</tr>\n"); // Close the table out.print("</table>\n"); // Add information about the running system out.print("<div class='systeminfo'>"); out.print("NetarchiveSuite " + Constants.getVersionString() + ", " + Settings.get(CommonSettings.ENVIRONMENT_NAME)); out.print("</div>"); // Close the page out.print("</body></html>"); } /** * Create a table element containing the given string, escaping HTML values in the process. * * @param s An unescaped string. Any HTML tags in this string will end up escaped away. * @return The same string escaped and enclosed in td tags. */ public static String makeTableElement(String s) { ArgumentNotValid.checkNotNull(s, "s"); return "<td>" + escapeHtmlValues(s) + "</td>"; } /** * Create a table header element containing the given string, escaping HTML values in the process. * * @param contents An unescaped string. Any HTML tags in this string will end up escaped away. * @return The same string escaped and enclosed in th tags. */ public static String makeTableHeader(String contents) { ArgumentNotValid.checkNotNull(contents, "contents"); return "<th>" + escapeHtmlValues(contents) + "</th>"; } /** * Create a table row. Note that in contrast to createTableElement and createTableHeader, the contents are not * escaped. They are expected to contain table elements. * * @param contents The contents to put into the table row. The entries will be delimited by newline characters. * @return The same string escaped and enclosed in td tags. */ public static String makeTableRow(String... contents) { ArgumentNotValid.checkNotNull(contents, "contents"); StringBuilder sb = new StringBuilder("<tr>"); for (String element : contents) { sb.append(element); sb.append("\n"); } sb.append("</tr>\n"); return sb.toString(); } /** * Get an HTML representation of the date given. * * @param d A date * @return A representation of the date that can be directly inserted into an HTML document, or the empty string if * d is null. * @deprecated Please use <fmt:date> from taglib instead. */ public static String makeDate(Date d) { if (d == null) { return ""; } else { return escapeHtmlValues(d.toString()); } } /** * Returns the toString() value of an object or a hyphen if the argument is null. * * @param o the given object * @return o.toString() or "-" if o is null */ public static String nullToHyphen(Object o) { if (o == null) { return "-"; } else { return o.toString(); } } /** * Escapes HTML special characters ", &, < and > (but not '). * * @param input a string * @return The string with values escaped. If input is null, the empty string is returned. */ public static String escapeHtmlValues(String input) { if (input == null) { return ""; } return input.replaceAll("&", "&").replaceAll("\\\"", """).replaceAll("<", "<").replaceAll(">", ">"); } /** * Encode a string for use in a URL, then escape characters that must be escaped in HTML. This must be used whenever * unknown strings are used in URLs that are placed in HTML. * * @param input A string * @return The same string, encoded to be safely placed in a URL in HTML. */ public static String encodeAndEscapeHTML(String input) { ArgumentNotValid.checkNotNull(input, "input"); return escapeHtmlValues(encode(input)); } /** * Escapes a string for use in javascript. Replaces " with \" and ' with \', so e.g. * escapeJavascriptQuotes("\"").equals("\\\"") Also, \ and any non-printable character is escaped for use in * javascript * * @param input a string * @return The string with values escaped. If input is null, the empty string is returned. */ public static String escapeJavascriptQuotes(String input) { if (input == null) { return ""; } input = input.replaceAll("\\\\", "\\\\\\\\"); input = input.replaceAll("\\\"", "\\\\\\\""); input = input.replaceAll("\\\'", "\\\\\\\'"); input = input.replaceAll("\\\u0000", "\\\\u0000"); input = input.replaceAll("\\\u0001", "\\\\u0001"); input = input.replaceAll("\\\u0002", "\\\\u0002"); input = input.replaceAll("\\\u0003", "\\\\u0003"); input = input.replaceAll("\\\u0004", "\\\\u0004"); input = input.replaceAll("\\\u0005", "\\\\u0005"); input = input.replaceAll("\\\u0006", "\\\\u0006"); input = input.replaceAll("\\\u0007", "\\\\u0007"); input = input.replaceAll("\\\b", "\\\\b"); input = input.replaceAll("\\\t", "\\\\t"); input = input.replaceAll("\\\n", "\\\\n"); // Note: \v is an escape for vertical tab that exists // in javascript but not in java input = input.replaceAll("\\\u000B", "\\\\v"); input = input.replaceAll("\\\f", "\\\\f"); input = input.replaceAll("\\\r", "\\\\r"); input = input.replaceAll("\\\u000E", "\\\\u000E"); input = input.replaceAll("\\\u000F", "\\\\u000F"); input = input.replaceAll("\\\u0010", "\\\\u0010"); input = input.replaceAll("\\\u0011", "\\\\u0011"); input = input.replaceAll("\\\u0012", "\\\\u0012"); input = input.replaceAll("\\\u0013", "\\\\u0013"); input = input.replaceAll("\\\u0014", "\\\\u0014"); input = input.replaceAll("\\\u0015", "\\\\u0015"); input = input.replaceAll("\\\u0016", "\\\\u0016"); input = input.replaceAll("\\\u0017", "\\\\u0017"); input = input.replaceAll("\\\u0018", "\\\\u0018"); input = input.replaceAll("\\\u0019", "\\\\u0019"); input = input.replaceAll("\\\u001A", "\\\\u001A"); input = input.replaceAll("\\\u001B", "\\\\u001B"); input = input.replaceAll("\\\u001C", "\\\\u001C"); input = input.replaceAll("\\\u001D", "\\\\u001D"); input = input.replaceAll("\\\u001E", "\\\\u001E"); input = input.replaceAll("\\\u001F", "\\\\u001F"); return input; } /** * Sets the character encoding for reading parameters and content from a request in a JSP page. * * @param request The servlet request object */ public static void setUTF8(HttpServletRequest request) { ArgumentNotValid.checkNotNull(request, "request"); // Why is this in an if block? Suppose we forward from a page where // we read file input from the request. Trying to set the character // encoding again here will throw an exception! // This is a bit of a hack - we know that _if_ we have set it, // we have set it to UTF-8, so this way we won't set it twice... if (request.getCharacterEncoding() == null || !request.getCharacterEncoding().equals("UTF-8")) { try { request.setCharacterEncoding("UTF-8"); } catch (UnsupportedEncodingException e) { throw new ArgumentNotValid("Should never happen! UTF-8 not supported", e); } } } /** * Given a URL in the sitesection hierarchy, returns the corresponding page title. * * @param url a given URL * @param locale the current locale * @return the corresponding page title, or string about "(no title)" if no title can be found * @throws ArgumentNotValid if the given url or locale is null or url is empty. */ public static String getTitle(String url, Locale locale) { ArgumentNotValid.checkNotNull(locale, "Locale locale"); ArgumentNotValid.checkNotNullOrEmpty(url, "String url"); for (SiteSection section : SiteSection.getSections()) { String title = section.getTitle(url, locale); if (title != null) { return title; } } log.warn("Could not find page title for page '" + url + "'"); return I18N.getString(locale, "pagetitle.unknown"); } /** * Get the (CSS) class name for a row in a table. The row count should start at 0. * * @param rowCount The number of the row * @return A CSS class name that should be the class of the TR element. */ public static String getRowClass(int rowCount) { if (rowCount % 6 < 3) { return "row0"; } else { return "row1"; } } /** * Get a locale from cookie, if present. The default request locale otherwise. * * @param request The request to get the locale for. * @return The cookie locale, if present. The default request locale otherwise. */ public static String getLocale(HttpServletRequest request) { ArgumentNotValid.checkNotNull(request, "request"); Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie c : cookies) { if (c.getName().equals("locale")) { return c.getValue(); } } } return request.getLocale().toString(); } /** * Forward to our standard error message page with an internationalized message. Note that this <em>doesn't</em> * throw ForwardedToErrorPage, it is the job of whoever calls this to do that if not within a JSP page (a JSP page * can just return immediately). All text involved will be HTML-escaped. * * @param context The context that the error happened in (the JSP-defined pageContext, typically) * @param I18N The i18n information * @param label An i18n label for the error. This label should begin with "errormsg;". * @param args Any extra args for i18n * @throws IOFailure If the forward fails */ public static void forwardWithErrorMessage(PageContext context, I18n I18N, String label, Object... args) { // Note that we may not want to be to strict here // as otherwise information could be lost. ArgumentNotValid.checkNotNull(context, "context"); ArgumentNotValid.checkNotNull(I18N, "I18N"); ArgumentNotValid.checkNotNull(label, "label"); ArgumentNotValid.checkNotNull(args, "args"); String msg = HTMLUtils.escapeHtmlValues(I18N.getString(context.getResponse().getLocale(), label, args)); context.getRequest().setAttribute("message", msg); RequestDispatcher rd = context.getServletContext().getRequestDispatcher("/message.jsp"); final String errormsg = "Failed to forward on error " + msg; try { rd.forward(context.getRequest(), context.getResponse()); } catch (IOException e) { log.warn(errormsg, e); throw new IOFailure(errormsg, e); } catch (ServletException e) { log.warn(errormsg, e); throw new IOFailure(errormsg, e); } } /** * Forward to our standard error message page with an internationalized message. Note that this <em>doesn't</em> * throw ForwardedToErrorPage, it is the job of whoever calls this to do that if not within a JSP page (a JSP page * can just return immediately). The text involved must be HTML-escaped before passing to this method. * * @param context The context that the error happened in (the JSP-defined pageContext, typically) * @param i18n The i18n information * @param label An i18n label for the error. This label should begin with "errormsg;". * @param args Any extra args for i18n. These must be valid HTML. * @throws IOFailure If the forward fails. */ public static void forwardWithRawErrorMessage(PageContext context, I18n i18n, String label, Object... args) { // Note that we may not want to be to strict here // as otherwise information could be lost. ArgumentNotValid.checkNotNull(context, "context"); ArgumentNotValid.checkNotNull(I18N, "I18N"); ArgumentNotValid.checkNotNull(label, "label"); ArgumentNotValid.checkNotNull(args, "args"); String msg = i18n.getString(context.getResponse().getLocale(), label, args); context.getRequest().setAttribute("message", msg); RequestDispatcher rd = context.getServletContext().getRequestDispatcher("/message.jsp"); try { rd.forward(context.getRequest(), context.getResponse()); } catch (IOException e) { final String errormsg = "Failed to forward on error " + msg; log.warn(errormsg, e); throw new IOFailure(errormsg, e); } catch (ServletException e) { final String errormsg = "Failed to forward on error " + msg; log.warn(errormsg, e); throw new IOFailure(errormsg, e); } } /** * Forward to our standard error message page with an internationalized message, in case of exception. Note that * this <em>doesn't</em> throw ForwardedToErrorPage, it is the job of whoever calls this to do that if not within a * JSP page (a JSP page can just return immediately). All text involved will be HTML-escaped. * * @param context The context that the error happened in (the JSP-defined pageContext, typically) * @param i18n The i18n information * @param e The exception that is being handled. * @param label An i18n label for the error. This label should begin with "errormsg;". * @param args Any extra args for i18n * @throws IOFailure If the forward fails */ public static void forwardWithErrorMessage(PageContext context, I18n i18n, Throwable e, String label, Object... args) { // Note that we may not want to be to strict here // as otherwise information could be lost. ArgumentNotValid.checkNotNull(context, "context"); ArgumentNotValid.checkNotNull(I18N, "I18N"); ArgumentNotValid.checkNotNull(label, "label"); ArgumentNotValid.checkNotNull(args, "args"); String msg = HTMLUtils.escapeHtmlValues(i18n.getString(context.getResponse().getLocale(), label, args)); context.getRequest().setAttribute("message", msg + "\n" + e.getLocalizedMessage()); RequestDispatcher rd = context.getServletContext().getRequestDispatcher("/message.jsp"); final String errormsg = "Failed to forward on error " + msg; try { rd.forward(context.getRequest(), context.getResponse()); } catch (IOException e1) { log.warn(errormsg, e1); throw new IOFailure(errormsg, e1); } catch (ServletException e1) { log.warn(errormsg, e1); throw new IOFailure(errormsg, e1); } } /** * Checks that the given parameters exist. If any of them do not exist, forwards to the error page and throws * ForwardedToErrorPage. * * @param context The context of the current JSP page * @param parameters List of parameters that must exist * @throws IOFailure If the forward fails * @throws ForwardedToErrorPage If a parameter is missing */ public static void forwardOnMissingParameter(PageContext context, String... parameters) throws ForwardedToErrorPage { // Note that we may not want to be to strict here // as otherwise information could be lost. ArgumentNotValid.checkNotNull(context, "context"); ArgumentNotValid.checkNotNull(parameters, "parameters"); ServletRequest request = context.getRequest(); for (String parameter : parameters) { String value = request.getParameter(parameter); if (value == null) { forwardWithErrorMessage(context, I18N, "errormsg;missing.parameter.0", parameter); throw new ForwardedToErrorPage("Missing parameter '" + parameter + "'"); } } } /** * Checks that the given parameters exist and are not empty. If any of them are missing or empty, forwards to the * error page and throws ForwardedToErrorPage. A parameter with only whitespace is considered empty. * * @param context The context of the current JSP page * @param parameters List of parameters that must exist and be non-empty * @throws IOFailure If the forward fails * @throws ForwardedToErrorPage if a parameter was missing or empty */ public static void forwardOnEmptyParameter(PageContext context, String... parameters) { // Note that we may not want to be to strict here // as otherwise information could be lost. ArgumentNotValid.checkNotNull(context, "context"); ArgumentNotValid.checkNotNull(parameters, "parameters"); forwardOnMissingParameter(context, parameters); ServletRequest request = context.getRequest(); for (String parameter : parameters) { String value = request.getParameter(parameter); if (value.trim().length() == 0) { forwardWithErrorMessage(context, I18N, "errormsg;empty.parameter.0", parameter); throw new ForwardedToErrorPage("Empty parameter '" + parameter + "'"); } } } /** * Checks that the given parameter exists and is one of a set of values. If is is missing or doesn't equal one of * the given values, forwards to the error page and throws ForwardedToErrorPage. * * @param context The context of the current JSP page * @param parameter parameter that must exist * @param legalValues legal values for the parameter * @throws IOFailure If the forward fails * @throws ForwardedToErrorPage if the parameter is none of the given values */ public static void forwardOnIllegalParameter(PageContext context, String parameter, String... legalValues) throws ForwardedToErrorPage { // Note that we may not want to be to strict here // as otherwise information could be lost. ArgumentNotValid.checkNotNull(context, "context"); ArgumentNotValid.checkNotNull(parameter, "parameter"); ArgumentNotValid.checkNotNull(legalValues, "legalValues"); forwardOnMissingParameter(context, parameter); String value = context.getRequest().getParameter(parameter); for (String legalValue : legalValues) { if (value.equals(legalValue)) { return; } } forwardWithErrorMessage(context, I18N, "errormsg;illegal.value.0.for.parameter.1", value, parameter); throw new ForwardedToErrorPage("Illegal value '" + value + "' for parameter '" + parameter + "'"); } /** * Parses a integer request parameter and checks that it lies within a given interval. If it doesn't, forwards to an * error page and throws ForwardedToErrorPage. * * @param context The context this call happens in * @param param A parameter to parse. * @param minValue The minimum allowed value * @param maxValue The maximum allowed value * @return The value x parsed from the string, if minValue <= x <= maxValue * @throws ForwardedToErrorPage if the parameter doesn't exist, is not a parseable integer, or doesn't lie within * the limits. */ public static int parseAndCheckInteger(PageContext context, String param, int minValue, int maxValue) throws ForwardedToErrorPage { // Note that we may not want to be to strict here // as otherwise information could be lost. ArgumentNotValid.checkNotNull(context, "context"); ArgumentNotValid.checkNotNull(param, "param"); Locale loc = HTMLUtils.getLocaleObject(context); forwardOnEmptyParameter(context, param); int value; String paramValue = context.getRequest().getParameter(param); try { value = NumberFormat.getInstance(loc).parse(paramValue).intValue(); if (value < minValue || value > maxValue) { forwardWithErrorMessage(context, I18N, "errormsg;parameter.0.outside.range.1.to.2.3", param, paramValue, minValue, maxValue); throw new ForwardedToErrorPage("Parameter '" + param + "' should be between " + minValue + " and " + maxValue + " but is " + paramValue); } return value; } catch (ParseException e) { forwardWithErrorMessage(context, I18N, "errormsg;parameter.0.not.an.integer.1", param, paramValue); throw new ForwardedToErrorPage("Invalid value " + paramValue + " for integer parameter '" + param + "'", e); } } /** * Parse an optionally present long-value from a request parameter. * * @param context The context of the web request. * @param param The name of the parameter to parse. * @param defaultValue A value to return if the parameter is not present (may be null). * @return Parsed value or default value if the parameter is missing or empty. Null will only be returned if passed * as the default value. * @throws ForwardedToErrorPage if the parameter is present but not parseable as a long value. */ public static Long parseOptionalLong(PageContext context, String param, Long defaultValue) { // Note that we may not want to be to strict here // as otherwise information could be lost. ArgumentNotValid.checkNotNull(context, "context"); ArgumentNotValid.checkNotNullOrEmpty(param, "String param"); Locale loc = HTMLUtils.getLocaleObject(context); String paramValue = context.getRequest().getParameter(param); return parseLong(loc, paramValue, param, defaultValue); } /** * Parse an optionally present date-value from a request parameter. * * @param context The context of the web request. * @param param The name of the parameter to parse * @param format The format of the date, in the format defined by SimpleDateFormat * @param defaultValue A value to return if the parameter is not present (may be null) * @return Parsed value or default value if the parameter is missing or empty. Null will only be returned if passed * as the default value. * @throws ForwardedToErrorPage if the parameter is present but not parseable as a date */ public static Date parseOptionalDate(PageContext context, String param, String format, Date defaultValue) { ArgumentNotValid.checkNotNullOrEmpty(param, "String param"); ArgumentNotValid.checkNotNullOrEmpty(format, "String format"); String paramValue = context.getRequest().getParameter(param); if (paramValue != null && paramValue.trim().length() > 0) { paramValue = paramValue.trim(); try { return new SimpleDateFormat(format).parse(paramValue); } catch (ParseException e) { forwardWithErrorMessage(context, I18N, "errormsg;parameter.0.not.a.date.with.format.1.2", param, format, paramValue); throw new ForwardedToErrorPage("Invalid value " + paramValue + " for date parameter '" + param + "' with format '" + format + "'", e); } } else { return defaultValue; } } /** * Parse an optionally present boolean from a request parameter. * * @param context The context of the web request. * @param param The name of the parameter to parse * @param defaultValue A value to return if the parameter is not present (may be null) * @return Parsed value or default value if the parameter is missing or empty. Null will only be returned if passed * as the default value. */ public static boolean parseOptionalBoolean(PageContext context, String param, boolean defaultValue) { ArgumentNotValid.checkNotNullOrEmpty(param, "String param"); String paramValue = context.getRequest().getParameter(param); if (paramValue != null && paramValue.trim().length() > 0) { paramValue = paramValue.trim(); return Boolean.parseBoolean(paramValue); } else { return defaultValue; } } /** * Create a localized string representation of the given long. * * @param i A long integer * @param context The given JSP context * @return a localized string representation of the given long TODO Should this method throw ArgumentNotValid if the * context is null */ public static String localiseLong(long i, PageContext context) { NumberFormat nf = NumberFormat.getInstance(HTMLUtils.getLocaleObject(context)); return nf.format(i); } /** * Create a localized string representation of the given long. * * @param i A long integer * @param locale The given locale. * @return a localized string representation of the given long TODO Should this method throw ArgumentNotValid if the * locale is null */ public static String localiseLong(long i, Locale locale) { NumberFormat nf = NumberFormat.getInstance(locale); return nf.format(i); } /** * Parse a given String for a long value. * * @param loc The given Locale. * @param paramValue The given parameter value * @param parameterName The given parameter name (used for debugging) * @param defaultValue The default value for the parameter, in case the string cannot be parsed * @return the long value found in the paramValue */ public static Long parseLong(Locale loc, String paramValue, String parameterName, Long defaultValue) { ArgumentNotValid.checkNotNull(loc, "Locale loc"); ArgumentNotValid.checkNotNullOrEmpty(parameterName, "String parameterName"); if (paramValue != null && paramValue.trim().length() > 0) { paramValue = paramValue.trim(); try { return NumberFormat.getInstance(loc).parse(paramValue).longValue(); } catch (ParseException e) { throw new ForwardedToErrorPage( "Invalid value " + paramValue + " for integer parameter '" + parameterName + "'", e); } } else { return defaultValue; } } }