001: package net.sourceforge.cruisecontrol.dashboard.widgets;
002:
003: import java.io.IOException;
004: import java.util.Arrays;
005: import java.util.HashMap;
006: import java.util.Iterator;
007: import java.util.List;
008: import java.util.Map;
009:
010: import javax.xml.parsers.ParserConfigurationException;
011: import javax.xml.parsers.SAXParser;
012: import javax.xml.parsers.SAXParserFactory;
013:
014: import net.sourceforge.cruisecontrol.dashboard.BuildMessage;
015: import net.sourceforge.cruisecontrol.dashboard.LogFile;
016: import net.sourceforge.cruisecontrol.dashboard.MessageLevel;
017: import net.sourceforge.cruisecontrol.dashboard.saxhandler.BuildMessageExtractor;
018: import net.sourceforge.cruisecontrol.dashboard.saxhandler.CompositeExtractor;
019: import net.sourceforge.cruisecontrol.dashboard.saxhandler.SAXBasedExtractor;
020: import net.sourceforge.cruisecontrol.dashboard.saxhandler.StackTraceExtractor;
021:
022: import org.apache.commons.lang.StringUtils;
023: import org.apache.commons.lang.StringEscapeUtils;
024: import org.xml.sax.SAXException;
025:
026: public class ErrorsAndWarningsMessagesWidget implements Widget {
027:
028: private final SAXBasedExtractor extractor;
029:
030: public ErrorsAndWarningsMessagesWidget() {
031: this (new CompositeExtractor(Arrays.asList(handlers())));
032: }
033:
034: private static SAXBasedExtractor[] handlers() {
035: return new SAXBasedExtractor[] { new BuildMessageExtractor(),
036: new StackTraceExtractor() };
037: }
038:
039: ErrorsAndWarningsMessagesWidget(SAXBasedExtractor extractor) {
040: this .extractor = extractor;
041: }
042:
043: public String getDisplayName() {
044: return "Errors and Warnings";
045: }
046:
047: public Object getOutput(Map parameters) {
048: try {
049: parseLogfile(parameters);
050: HashMap props = new HashMap();
051: extractor.report(props);
052: return parseMessage(props);
053:
054: } catch (Throwable e) {
055: throw new RuntimeException(e);
056: }
057: }
058:
059: private void parseLogfile(Map parameters)
060: throws ParserConfigurationException, SAXException,
061: IOException {
062: SAXParser saxParser = SAXParserFactory.newInstance()
063: .newSAXParser();
064: LogFile logFile = (LogFile) parameters
065: .get(Widget.PARAM_BUILD_LOG_FILE);
066: saxParser.parse(logFile.getInputStream(), extractor);
067: }
068:
069: private String parseMessage(Map props) {
070: String replaced = buildError(props, HTML_TEMPLATE_START);
071: replaced = errorsAndWarnings((List) props
072: .get(BuildMessageExtractor.KEY_MESSAGES), replaced);
073: replaced = stacktrace(props, replaced);
074: return replaced + "</div>";
075: }
076:
077: private String errorsAndWarnings(List messages, String currentHtml) {
078: StringBuffer sb = new StringBuffer();
079: for (Iterator iter = messages.iterator(); iter.hasNext();) {
080: BuildMessage message = (BuildMessage) iter.next();
081: MessageLevel level = message.getLevel();
082: if (MessageLevel.WARN.equals(level)
083: || MessageLevel.ERROR.equals(level)) {
084: sb.append(message.getMessage()).append("<br/>");
085: }
086: }
087: String error = StringUtils.defaultIfEmpty(sb.toString(),
088: "No errors or warnings");
089:
090: String errorsAndWarningsHtml = StringUtils.replace(
091: ERRORS_AND_WARNINGS_HTML, "$errors", StringEscapeUtils
092: .escapeHtml(error));
093: boolean hasErrorsOrWarnings = !StringUtils.isEmpty(sb
094: .toString());
095: return currentHtml
096: + makeToggleable(errorsAndWarningsHtml,
097: "errors_and_warnings_element",
098: hasErrorsOrWarnings);
099: }
100:
101: private String makeToggleable(String htmlSnippet, String element,
102: boolean shouldToggle) {
103: String className = shouldToggle ? "class=\"collapsible_title title_message_collapsed\""
104: : "";
105: String style = shouldToggle ? "style='display:none;'" : "";
106: String nextElementClassName = shouldToggle ? "class='collapsible_content'"
107: : "";
108: String newSnippet = htmlSnippet;
109: newSnippet = StringUtils.replace(newSnippet, "$className",
110: className);
111: newSnippet = StringUtils.replace(newSnippet, "$style", style);
112: newSnippet = StringUtils.replace(newSnippet,
113: "$nextElementClassName", nextElementClassName);
114: return newSnippet;
115: }
116:
117: private String stacktrace(Map props, String currentHtml) {
118: boolean hasStacktrace = !StringUtils.isEmpty(props.get(
119: StackTraceExtractor.KEY_STACKTRACE).toString());
120: String stacktrace = StringUtils.replace(STACKTRACE_HTML,
121: "$stacktrace", getMessage(props,
122: StackTraceExtractor.KEY_STACKTRACE,
123: "No stacktrace"));
124: return currentHtml
125: + makeToggleable(stacktrace, "stacktrace",
126: hasStacktrace);
127: }
128:
129: private String buildError(Map props, String currentHtml) {
130: String buildErrorMessage = StringUtils.replace(
131: BUILD_ERROR_MESSAGE_HTML, "$buildError", getMessage(
132: props, StackTraceExtractor.KEY_ERROR,
133: "No error message"));
134: return currentHtml + buildErrorMessage;
135: }
136:
137: private String getMessage(Map props, String key, String defaultMsg) {
138: return StringUtils.defaultIfEmpty(props.get(key).toString(),
139: defaultMsg);
140: }
141:
142: private static final String HTML_TEMPLATE_START = "<div>";
143: private static final String BUILD_ERROR_MESSAGE_HTML = "<h2>Build Error Message</h2>$buildError<hr/>";
144: private static final String ERRORS_AND_WARNINGS_HTML = "<h2 $className>Errors and Warnings</h2>"
145: + "<div id='errors_and_warnings_element' $nextElementClassName $style><pre>$errors</pre></div><hr/>";
146: private static final String STACKTRACE_HTML = "<h2 $className>Stacktrace</h2>"
147: + "<div $nextElementClassName $style><pre>$stacktrace</pre></div>";
148:
149: }
|