package net.sourceforge.cruisecontrol.dashboard.widgets;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import net.sourceforge.cruisecontrol.dashboard.BuildMessage;
import net.sourceforge.cruisecontrol.dashboard.LogFile;
import net.sourceforge.cruisecontrol.dashboard.MessageLevel;
import net.sourceforge.cruisecontrol.dashboard.saxhandler.BuildMessageExtractor;
import net.sourceforge.cruisecontrol.dashboard.saxhandler.CompositeExtractor;
import net.sourceforge.cruisecontrol.dashboard.saxhandler.SAXBasedExtractor;
import net.sourceforge.cruisecontrol.dashboard.saxhandler.StackTraceExtractor;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.xml.sax.SAXException;
public class ErrorsAndWarningsMessagesWidget implements Widget {
private final SAXBasedExtractor extractor;
public ErrorsAndWarningsMessagesWidget() {
this(new CompositeExtractor(Arrays.asList(handlers())));
}
private static SAXBasedExtractor[] handlers() {
return new SAXBasedExtractor[] {new BuildMessageExtractor(), new StackTraceExtractor()};
}
ErrorsAndWarningsMessagesWidget(final SAXBasedExtractor extractor) {
this.extractor = extractor;
}
public String getDisplayName() {
return "Errors and Warnings";
}
public Object getOutput(final Map parameters) {
try {
parseLogfile(parameters);
final HashMap props = new HashMap();
extractor.report(props);
return parseMessage(props);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
private void parseLogfile(final Map parameters) throws ParserConfigurationException, SAXException, IOException {
final SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
final LogFile logFile = (LogFile) parameters.get(Widget.PARAM_BUILD_LOG_FILE);
saxParser.parse(logFile.getInputStream(), extractor);
}
private String parseMessage(final Map props) {
String replaced = buildError(props);
replaced = errorsAndWarnings((List<BuildMessage>) props.get(BuildMessageExtractor.KEY_MESSAGES), replaced);
replaced = stacktrace(props, replaced);
return replaced + "</div>";
}
private String errorsAndWarnings(final List<BuildMessage> messages, final String currentHtml) {
final StringBuffer sb = new StringBuffer();
for (final BuildMessage message : messages) {
final MessageLevel level = message.getLevel();
if (MessageLevel.WARN.equals(level) || MessageLevel.ERROR.equals(level)) {
sb.append(message.getMessage()).append("\n"); // RHT 08/05/2008 was <br/>
}
}
final String error = StringUtils.defaultIfEmpty(sb.toString(), "No errors or warnings");
final String errorsAndWarningsHtml = StringUtils.replace(ERRORS_AND_WARNINGS_HTML, "$errors",
StringEscapeUtils.escapeHtml(error));
final boolean hasErrorsOrWarnings = !StringUtils.isEmpty(sb.toString());
return currentHtml + makeToggleable(errorsAndWarningsHtml, hasErrorsOrWarnings);
}
private String makeToggleable(final String htmlSnippet, final boolean shouldToggle) {
final String className = shouldToggle
? "class=\"collapsible_title title_message_collapsed\""
: "";
final String style = shouldToggle
? "style='display:none;'"
: "";
final String nextElementClassName = shouldToggle
? "class='collapsible_content'"
: "";
String newSnippet = htmlSnippet;
newSnippet = StringUtils.replace(newSnippet, "$className", className);
newSnippet = StringUtils.replace(newSnippet, "$style", style);
newSnippet = StringUtils.replace(newSnippet, "$nextElementClassName", nextElementClassName);
return newSnippet;
}
private String stacktrace(final Map props, final String currentHtml) {
final boolean hasStacktrace = !StringUtils.isEmpty(props.get(StackTraceExtractor.KEY_STACKTRACE).toString());
final String stacktrace = StringUtils.replace(STACKTRACE_HTML, "$stacktrace",
getMessage(props, StackTraceExtractor.KEY_STACKTRACE, "No stacktrace"));
return currentHtml + makeToggleable(stacktrace, hasStacktrace);
}
private String buildError(final Map props) {
final String buildErrorMessage = StringUtils.replace(BUILD_ERROR_MESSAGE_HTML, "$buildError",
getMessage(props, StackTraceExtractor.KEY_ERROR, "No error message"));
return ErrorsAndWarningsMessagesWidget.HTML_TEMPLATE_START + buildErrorMessage;
}
private String getMessage(final Map props, final String key, final String defaultMsg) {
return StringUtils.defaultIfEmpty(props.get(key).toString(), defaultMsg);
}
private static final String HTML_TEMPLATE_START =
"<div>";
private static final String BUILD_ERROR_MESSAGE_HTML =
"<h2>Build Error Message</h2>$buildError<hr/>";
private static final String ERRORS_AND_WARNINGS_HTML =
"<h2 $className>Errors and Warnings</h2>"
+ "<div id='errors_and_warnings_element' $nextElementClassName $style><pre>$errors</pre></div><hr/>";
private static final String STACKTRACE_HTML =
"<h2 $className>Stacktrace</h2>"
+ "<div $nextElementClassName $style><pre>$stacktrace</pre></div>";
}