diff options
Diffstat (limited to 'src/de/lmu/ifi/dbs/elki/gui/util/LogPane.java')
-rw-r--r-- | src/de/lmu/ifi/dbs/elki/gui/util/LogPane.java | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/src/de/lmu/ifi/dbs/elki/gui/util/LogPane.java b/src/de/lmu/ifi/dbs/elki/gui/util/LogPane.java new file mode 100644 index 00000000..058ac207 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/gui/util/LogPane.java @@ -0,0 +1,251 @@ +package de.lmu.ifi.dbs.elki.gui.util; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.awt.Color; +import java.util.logging.ErrorManager; +import java.util.logging.Formatter; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.SimpleFormatter; + +import javax.swing.JTextPane; +import javax.swing.text.Style; +import javax.swing.text.StyleConstants; + +import de.lmu.ifi.dbs.elki.logging.LoggingConfiguration; +import de.lmu.ifi.dbs.elki.logging.MessageFormatter; +import de.lmu.ifi.dbs.elki.logging.OutputStreamLogger; +import de.lmu.ifi.dbs.elki.logging.progress.ProgressLogRecord; + +/** + * A Swing object to receive ELKI logging output. Call + * {@link #becomeDefaultLogger()} to register as default logger in ELKI. + * + * @author Erich Schubert + * + * @apiviz.uses LoggingConfiguration + * @apiviz.uses de.lmu.ifi.dbs.elki.logging.ELKILogRecord + * @apiviz.uses ProgressLogRecord + */ +public class LogPane extends JTextPane { + /** + * Serialization version number + */ + private static final long serialVersionUID = 1L; + + /** + * Base (default) style + */ + protected Style baseStyle; + + /** + * Regular message style + */ + protected Style msgStyle; + + /** + * Debug message style + */ + protected Style dbgStyle; + + /** + * Error message style + */ + protected Style errStyle; + + /** + * Formatter for regular messages (informational records) + */ + private Formatter msgformat = new MessageFormatter(); + + /** + * Formatter for debugging messages + */ + private Formatter debugformat = new SimpleFormatter(); + + /** + * Formatter for error messages + */ + private Formatter errformat = new SimpleFormatter(); + + /** + * Last newline position + */ + private int lastNewlinePos = 0; + + /** + * Constructor + */ + public LogPane() { + super(); + // setup styles + baseStyle = getStyledDocument().addStyle(null, null); + msgStyle = getStyledDocument().addStyle("msg", baseStyle); + errStyle = getStyledDocument().addStyle("err", baseStyle); + errStyle.addAttribute(StyleConstants.Foreground, Color.RED); + dbgStyle = getStyledDocument().addStyle("err", baseStyle); + dbgStyle.addAttribute(StyleConstants.Foreground, Color.BLUE); + } + + /** + * Print a message as if it were logged, without going through the full + * logger. + * + * @param message Message text + * @param level Message level + */ + public void publish(String message, Level level) { + try { + publish(new LogRecord(level, message)); + } + catch(Exception e) { + throw new RuntimeException("Error writing a log-like message.", e); + } + } + + /** + * Publish a log record to the logging pane. + * + * @param record Log record + * @throws Exception + */ + protected synchronized void publish(LogRecord record) throws Exception { + // choose an appropriate formatter + final Formatter fmt; + final Style style; + // always format progress messages using the progress formatter. + if(record.getLevel().intValue() >= Level.WARNING.intValue()) { + // format errors using the error formatter + fmt = errformat; + style = errStyle; + } + else if(record.getLevel().intValue() <= Level.FINE.intValue()) { + // format debug statements using the debug formatter. + fmt = debugformat; + style = dbgStyle; + } + else { + // default to the message formatter. + fmt = msgformat; + style = msgStyle; + } + // format + final String m; + m = fmt.format(record); + if(record instanceof ProgressLogRecord) { + if(lastNewlinePos < getStyledDocument().getLength()) { + getStyledDocument().remove(lastNewlinePos, getStyledDocument().getLength() - lastNewlinePos); + } + } + else { + // insert a newline, if we didn't see one yet. + if(lastNewlinePos < getStyledDocument().getLength()) { + getStyledDocument().insertString(getStyledDocument().getLength(), "\n", style); + lastNewlinePos = getStyledDocument().getLength(); + } + } + int tail = tailingNonNewline(m, 0, m.length()); + int headlen = m.length() - tail; + if(headlen > 0) { + String pre = m.substring(0, headlen); + getStyledDocument().insertString(getStyledDocument().getLength(), pre, style); + } + lastNewlinePos = getStyledDocument().getLength(); + if(tail > 0) { + String post = m.substring(m.length() - tail); + getStyledDocument().insertString(lastNewlinePos, post, style); + } + } + + /** + * Count the tailing non-newline characters. + * + * @param str String + * @param off Offset + * @param len Range + * @return number of tailing non-newline character + */ + private int tailingNonNewline(String str, int off, int len) { + for(int cnt = 0; cnt < len; cnt++) { + final int pos = off + (len - 1) - cnt; + if(str.charAt(pos) == OutputStreamLogger.UNIX_NEWLINE) { + return cnt; + } + // TODO: need to compare to NEWLINE, too? + } + return len; + } + + /** + * Clear the current contents. + */ + public void clear() { + setText(""); + lastNewlinePos = 0; + } + + /** + * Become the default logger. + */ + public void becomeDefaultLogger() { + LoggingConfiguration.replaceDefaultHandler(new LogPaneHandler()); + } + + /** + * Internal {@link java.util.logging.Handler} + * + * @author Erich Schubert + * + * @apiviz.exclude + */ + private class LogPaneHandler extends Handler { + /** + * Constructor. + */ + protected LogPaneHandler() { + super(); + } + + @Override + public void close() throws SecurityException { + // do nothing + } + + @Override + public void flush() { + // do nothing + } + + @Override + public void publish(LogRecord record) { + try { + LogPane.this.publish(record); + } + catch(Exception e) { + reportError("Error printing output log message.", e, ErrorManager.GENERIC_FAILURE); + } + } + } +} |