/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2006-2009 Sun Microsystems, Inc. */ package org.opends.quicksetup.util; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.logging.Level; import java.util.logging.Logger; import org.opends.quicksetup.ui.UIFactory; import org.opends.quicksetup.Constants; import static org.opends.messages.QuickSetupMessages.*; import org.opends.messages.Message; import org.opends.messages.MessageBuilder; /** * This is an implementation of the ProgressMessageFormatter class that * provides format in HTML. * */ public class HtmlProgressMessageFormatter implements ProgressMessageFormatter { static private final Logger LOG = Logger.getLogger(HtmlProgressMessageFormatter.class.getName()); private Message doneHtml; private Message errorHtml; /** * The constant used to separate parameters in an URL. */ private static final String PARAM_SEPARATOR = "&&&&"; /** * The space in HTML. */ private static final Message SPACE = Message.raw(" "); /** * The line break. * The extra char is necessary because of bug: * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4988885 */ private static final Message LINE_BREAK= Message.raw(" "+Constants.HTML_LINE_BREAK); private static final Message TAB = new MessageBuilder(SPACE) .append(SPACE) .append(SPACE) .append(SPACE) .append(SPACE) .toMessage(); /** * Returns the HTML representation of the text without providing any style. * @param text the source text from which we want to get the HTML * representation * @return the HTML representation for the given text. */ public Message getFormattedText(Message text) { return Message.raw(Utils.getHtml(String.valueOf(text))); } /** * Returns the HTML representation of the text that is the summary of the * installation process (the one that goes in the UI next to the progress * bar). * @param text the source text from which we want to get the formatted * representation * @return the HTML representation of the summary for the given text. */ public Message getFormattedSummary(Message text) { return new MessageBuilder("<html>") .append(UIFactory.applyFontToHtml( String.valueOf(text), UIFactory.PROGRESS_FONT)) .toMessage(); } /** * Returns the HTML representation of an error for a given text. * @param text the source text from which we want to get the HTML * representation * @param applyMargin specifies whether we apply a margin or not to the * resulting HTML. * @return the HTML representation of an error for the given text. */ public Message getFormattedError(Message text, boolean applyMargin) { String html; if (!Utils.containsHtml(String.valueOf(text))) { html = UIFactory.getIconHtml(UIFactory.IconType.ERROR_LARGE) + SPACE + SPACE + UIFactory.applyFontToHtml(Utils.getHtml(String.valueOf(text)), UIFactory.PROGRESS_ERROR_FONT); } else { html = UIFactory.getIconHtml(UIFactory.IconType.ERROR_LARGE) + SPACE + SPACE + UIFactory.applyFontToHtml( String.valueOf(text), UIFactory.PROGRESS_FONT); } String result = UIFactory.applyErrorBackgroundToHtml(html); if (applyMargin) { result = UIFactory.applyMargin(result, UIFactory.TOP_INSET_ERROR_MESSAGE, 0, 0, 0); } return Message.raw(result); } /** * Returns the HTML representation of a warning for a given text. * @param text the source text from which we want to get the HTML * representation * @param applyMargin specifies whether we apply a margin or not to the * resulting HTML. * @return the HTML representation of a warning for the given text. */ public Message getFormattedWarning(Message text, boolean applyMargin) { String html; if (!Utils.containsHtml(String.valueOf(text))) { html = UIFactory.getIconHtml(UIFactory.IconType.WARNING_LARGE) + SPACE + SPACE + UIFactory.applyFontToHtml(Utils.getHtml(String.valueOf(text)), UIFactory.PROGRESS_WARNING_FONT); } else { html = UIFactory.getIconHtml(UIFactory.IconType.WARNING_LARGE) + SPACE + SPACE + UIFactory.applyFontToHtml( String.valueOf(text), UIFactory.PROGRESS_FONT); } String result = UIFactory.applyWarningBackgroundToHtml(html); if (applyMargin) { result = UIFactory.applyMargin(result, UIFactory.TOP_INSET_ERROR_MESSAGE, 0, 0, 0); } return Message.raw(result); } /** * Returns the HTML representation of a success message for a given text. * @param text the source text from which we want to get the HTML * representation * @return the HTML representation of a success message for the given text. */ public Message getFormattedSuccess(Message text) { // Note: the text we get already is in HTML form String html = UIFactory.getIconHtml(UIFactory.IconType.INFORMATION_LARGE) + SPACE + SPACE + UIFactory.applyFontToHtml(String.valueOf(text), UIFactory.PROGRESS_FONT); return Message.raw(UIFactory.applySuccessfulBackgroundToHtml(html)); } /** * Returns the HTML representation of a log error message for a given * text. * @param text the source text from which we want to get the HTML * representation * @return the HTML representation of a log error message for the given * text. */ public Message getFormattedLogError(Message text) { String html = Utils.getHtml(String.valueOf(text)); return Message.raw(UIFactory.applyFontToHtml(html, UIFactory.PROGRESS_LOG_ERROR_FONT)); } /** * Returns the HTML representation of a log message for a given text. * @param text the source text from which we want to get the HTML * representation * @return the HTML representation of a log message for the given text. */ public Message getFormattedLog(Message text) { String html = Utils.getHtml(String.valueOf(text)); return Message.raw(UIFactory.applyFontToHtml(html, UIFactory.PROGRESS_LOG_FONT)); } /** * Returns the HTML representation of the 'Done' text string. * @return the HTML representation of the 'Done' text string. */ public Message getFormattedDone() { if (doneHtml == null) { String html = Utils.getHtml(INFO_PROGRESS_DONE.get().toString()); doneHtml = Message.raw(UIFactory.applyFontToHtml(html, UIFactory.PROGRESS_DONE_FONT)); } return Message.raw(doneHtml); } /** * Returns the HTML representation of the 'Error' text string. * @return the HTML representation of the 'Error' text string. */ public Message getFormattedError() { if (errorHtml == null) { String html = Utils.getHtml(INFO_PROGRESS_ERROR.get().toString()); errorHtml = Message.raw(UIFactory.applyFontToHtml(html, UIFactory.PROGRESS_ERROR_FONT)); } return Message.raw(errorHtml); } /** * Returns the HTML representation of the argument text to which we add * points. For instance if we pass as argument 'Configuring Server' the * return value will be 'Configuring Server <B>.....</B>'. * @param text the String to which add points. * @return the HTML representation of the '.....' text string. */ public Message getFormattedWithPoints(Message text) { String html = Utils.getHtml(String.valueOf(text)); String points = SPACE + Utils.getHtml(INFO_PROGRESS_POINTS.get().toString()) + SPACE; MessageBuilder buf = new MessageBuilder(); buf.append(UIFactory.applyFontToHtml(html, UIFactory.PROGRESS_FONT)) .append( UIFactory.applyFontToHtml(points, UIFactory.PROGRESS_POINTS_FONT)); return buf.toMessage(); } /** * Returns the formatted representation of a point. * @return the formatted representation of the '.' text string. */ public Message getFormattedPoint() { return Message.raw(UIFactory.applyFontToHtml(".", UIFactory.PROGRESS_POINTS_FONT)); } /** * Returns the formatted representation of a space. * @return the formatted representation of the ' ' text string. */ public Message getSpace() { return Message.raw(SPACE); } /** * Returns the formatted representation of a progress message for a given * text. * @param text the source text from which we want to get the formatted * representation * @return the formatted representation of a progress message for the given * text. */ public Message getFormattedProgress(Message text) { return Message.raw(UIFactory.applyFontToHtml( Utils.getHtml(String.valueOf(text)), UIFactory.PROGRESS_FONT)); } /** * Returns the HTML representation of an error message for a given throwable. * This method applies a margin if the applyMargin parameter is * <CODE>true</CODE>. * @param t the throwable. * @param applyMargin specifies whether we apply a margin or not to the * resulting HTML. * @return the HTML representation of an error message for the given * exception. */ public Message getFormattedError(Throwable t, boolean applyMargin) { String openDiv = "<div style=\"margin-left:5px; margin-top:10px\">"; String hideText = UIFactory.applyFontToHtml(INFO_HIDE_EXCEPTION_DETAILS.get().toString(), UIFactory.PROGRESS_FONT); String showText = UIFactory.applyFontToHtml(INFO_SHOW_EXCEPTION_DETAILS.get().toString(), UIFactory.PROGRESS_FONT); String closeDiv = "</div>"; StringBuilder stackBuf = new StringBuilder(); stackBuf.append(getHtmlStack(t)); Throwable root = t.getCause(); while (root != null) { stackBuf.append(Utils.getHtml(INFO_EXCEPTION_ROOT_CAUSE.get().toString())) .append(getLineBreak()); stackBuf.append(getHtmlStack(root)); root = root.getCause(); } String stackText = UIFactory.applyFontToHtml(stackBuf.toString(), UIFactory.STACK_FONT); StringBuilder buf = new StringBuilder(); String msg = t.getMessage(); if (msg != null) { buf.append(UIFactory.applyFontToHtml(Utils.getHtml(t.getMessage()), UIFactory.PROGRESS_ERROR_FONT)).append(getLineBreak()); } else { buf.append(t.toString()).append(getLineBreak()); } buf.append(getErrorWithStackHtml(openDiv, hideText, showText, stackText, closeDiv, false)); String html = UIFactory.getIconHtml(UIFactory.IconType.ERROR_LARGE) + SPACE + SPACE + buf.toString(); String result; if (applyMargin) { result = UIFactory.applyMargin(UIFactory.applyErrorBackgroundToHtml(html), UIFactory.TOP_INSET_ERROR_MESSAGE, 0, 0, 0); } else { result = UIFactory.applyErrorBackgroundToHtml(html); } return Message.raw(result); } /** * Returns the line break in HTML. * @return the line break in HTML. */ public Message getLineBreak() { return LINE_BREAK; } /** * Returns the tab in HTML. * @return the tab in HTML. */ public Message getTab() { return TAB; } /** * Returns the task separator in HTML. * @return the task separator in HTML. */ public Message getTaskSeparator() { return Message.raw(UIFactory.HTML_SEPARATOR); } /** * Returns the log HTML representation after the user has clicked on a url. * * @see HtmlProgressMessageFormatter#getErrorWithStackHtml * @param url that has been clicked * @param lastText the HTML representation of the log before clicking on the * url. * @return the log HTML representation after the user has clicked on a url. */ public Message getFormattedAfterUrlClick(String url, Message lastText) { String urlText = getErrorWithStackHtml(url, false); String newUrlText = getErrorWithStackHtml(url, true); String lastTextStr = String.valueOf(lastText); int index = lastTextStr.indexOf(urlText); if (index == -1) { LOG.log(Level.FINE, "lastText: " + lastText + "does not contain: " + urlText); } else { lastTextStr = lastTextStr.substring(0, index) + newUrlText + lastTextStr.substring(index + urlText.length()); } return Message.raw(lastTextStr); } /** * Returns a HTML representation of the stack trace of a Throwable object. * @param ex the throwable object from which we want to obtain the stack * trace HTML representation. * @return a HTML representation of the stack trace of a Throwable object. */ private String getHtmlStack(Throwable ex) { StringBuilder buf = new StringBuilder(); buf.append(SPACE) .append(SPACE) .append(SPACE) .append(SPACE) .append(SPACE) .append(SPACE) .append(SPACE) .append(SPACE) .append(SPACE) .append(SPACE) .append(Utils.getHtml(ex.toString())) .append(getLineBreak()); StackTraceElement[] stack = ex.getStackTrace(); for (StackTraceElement aStack : stack) { buf.append(SPACE) .append(SPACE) .append(SPACE) .append(SPACE) .append(SPACE) .append(SPACE) .append(SPACE) .append(SPACE) .append(SPACE) .append(SPACE) .append(Utils.getHtml(aStack.toString())) .append(getLineBreak()); } return buf.toString(); } /** * Returns the HTML representation of an exception in the * progress log.<BR> * We can have something of type:<BR><BR> * * An error occurred. java.io.IOException could not connect to server.<BR> * <A HREF="">Show Details</A> * * When the user clicks on 'Show Details' the whole stack will be displayed. * * An error occurred. java.io.IOException could not connect to server.<BR> * <A HREF="">Hide Details</A><BR> * ... And here comes all the stack trace representation<BR> * * * As the object that listens to this hyperlink events is not here (it is * QuickSetupStepPanel) we must include all the information somewhere. The * chosen solution is to include everything in the URL using parameters. * This everything consists of: * The open div tag for the text. * The text that we display when we do not display the exception. * The text that we display when we display the exception. * The stack trace text. * The closing div. * A boolean informing if we are hiding the exception or not (to know in the * next event what must be displayed). * * @param openDiv the open div tag for the text. * @param hideText the text that we display when we do not display the * exception. * @param showText the text that we display when we display the exception. * @param stackText the stack trace text. * @param closeDiv the closing div. * @param hide a boolean informing if we are hiding the exception or not. * @return the HTML representation of an error message with an stack trace. */ private String getErrorWithStackHtml(String openDiv, String hideText, String showText, String stackText, String closeDiv, boolean hide) { StringBuilder buf = new StringBuilder(); String params = getUrlParams(openDiv, hideText, showText, stackText, closeDiv, hide); try { String text = hide ? hideText : showText; buf.append(openDiv).append("<a href=\"http://") .append(URLEncoder.encode(params, "UTF-8")) .append("\">").append(text).append("</a>"); if (hide) { buf.append(getLineBreak()).append(stackText); } buf.append(closeDiv); } catch (UnsupportedEncodingException uee) { // Bug throw new IllegalStateException("UTF-8 is not supported ", uee); } return buf.toString(); } /** * Gets the url parameters of the href we construct in getErrorWithStackHtml. * @see HtmlProgressMessageFormatter#getErrorWithStackHtml * @param openDiv the open div tag for the text. * @param hideText the text that we display when we do not display the * exception. * @param showText the text that we display when we display the exception. * @param stackText the stack trace text. * @param closeDiv the closing div. * @param hide a boolean informing if we are hiding the exception or not. * @return the url parameters of the href we construct in getHrefString. */ private String getUrlParams(String openDiv, String hideText, String showText, String stackText, String closeDiv, boolean hide) { StringBuilder buf = new StringBuilder(); buf.append(openDiv).append(PARAM_SEPARATOR); buf.append(hideText).append(PARAM_SEPARATOR); buf.append(showText).append(PARAM_SEPARATOR); buf.append(stackText).append(PARAM_SEPARATOR); buf.append(closeDiv).append(PARAM_SEPARATOR); buf.append(hide); return buf.toString(); } /** * Returns the HTML representation of an exception in the * progress log for a given url. * @param url the url containing all the information required to retrieve * the HTML representation. * @param inverse indicates whether we want to 'inverse' the representation * or not. For instance if the url specifies that the stack is being hidden * and this parameter is <CODE>true</CODE> the resulting HTML will display * the stack. * @return the HTML representation of an exception in the progress log for a * given url. */ private String getErrorWithStackHtml(String url, boolean inverse) { String p = url.substring("http://".length()); try { p = URLDecoder.decode(p, "UTF-8"); } catch (UnsupportedEncodingException uee) { // Bug throw new IllegalStateException("UTF-8 is not supported ", uee); } String params[] = p.split(PARAM_SEPARATOR); int i = 0; String openDiv = params[i++]; String hideText = params[i++]; String showText = params[i++]; String stackText = params[i++]; String closeDiv = params[i++]; boolean isHide = Boolean.valueOf(params[i]); if (isHide) { return getErrorWithStackHtml(openDiv, hideText, showText, stackText, closeDiv, !inverse); } else { return getErrorWithStackHtml(openDiv, hideText, showText, stackText, closeDiv, inverse); } } }