/*
* This file is part of the OpenJML plugin project.
* Copyright (c) 2004-2013 David R. Cok
*/
package org.jmlspecs.openjml.eclipse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.console.MessageConsole;
import org.eclipse.ui.console.MessageConsoleStream;
import org.eclipse.ui.progress.UIJob;
/** This class provides a Log.IListener implementation that sends
* text written through methods in Log to the Eclipse console.
*/
public class ConsoleLogger implements Log.IListener {
public static final String jobName = "Console Logger"; //$NON-NLS-1$
/** Creates a new ConsoleLogger utility object
*
* @param consoleName The name of the console to be logged to, as it will
* appear in the Eclipse UI
*/
//@ modifies this.*;
public ConsoleLogger() {
Plugin plugin = Activator.getDefault();
this.pluginLog = plugin.getLog();
this.pluginID = plugin.getBundle().getSymbolicName();
}
// This model variable models the content of the material
// sent to the log.
//@ model public String content;
/** The ILog of the plugin that this ConsoleLogger object connects to */
//@ constraint \not_modified(pluginLog);
//@ invariant pluginLog != null;
//@ spec_public
final private ILog pluginLog;
/** The id of the plugin using this log */
//@ constraint \not_modified(plugiID);
//@ invariant pluginID != null;
//@ spec_public
final private String pluginID;
/** If true, then informational output is recorded in the
* system log (in addition to the console); if false, then it is not.
*/
public boolean alsoLogInfo = false;
/** Cached value of the stream to use to write to the console */
private MessageConsoleStream stream = null;
//@ private constraint \old(stream) != null ==> \not_modified(stream);
/** Returns the output stream for the IListener interface. */
@Override
public OutputStream getStream() {
return getConsoleStream();
}
/** Creates, if necessary, and returns an instance of
* the stream to use to write to the console
* @return The stream value to use
*/
//@ ensures \result != null;
public MessageConsoleStream getConsoleStream() {
MessageConsole console = ConsoleFactory.getJMLConsole(true);
if (stream == null)stream = console.newMessageStream();
return stream;
}
/** Color to use for error messages */
static final private Color errorColor = new Color(null,255,0,0);
// In the implementations below we write to the console in the UI thread.
// This ensures that messages to the user appear in an understandable
// order. If we are already in the UI thread we definitely do not want
// to write a message in a new job, because that would not execute until
// the current job is complete.
// FIXME - Whoops - there seems to be some interaction with the progress monitor - figure this out
boolean test = false;
/**
* Records an informational message adding a newline
* @param msg The message to log
*/
//@ requires msg != null;
//@ modifies content;
@Override
public void log(final String msg) {
log_helper(msg,true,false,null);
}
/**
* Records an informational message without a terminating new line.
* If a message is written to the System log, it will be written as
* an individual message, despite the absence of a newline.
* @param msg The message to log
*/
//@ requires msg != null;
//@ modifies content;
public void log_noln(final String msg) {
log_helper(msg,false,false,null);
}
// FIXME - change to logln, errorlogln?
/**
* Records an error message; only call this from the UI thread (because it
* changes the color associated with the console).
* @param msg The message to log (adding a newline)
* @param e An associated exception (may be null)
*/
//@ modifies content;
public void errorlog(final /*@ non_null */String msg, final /*@ nullable */ Throwable e) {
log_helper(msg,true,true,e);
}
/** Internal helper method to avoid replicating functionality */
private void log_helper(final String msg, boolean newline, boolean error, final Throwable e) {
MessageConsoleStream cs = getConsoleStream();
if (test && Display.getDefault().getThread() != Thread.currentThread() ) {
UIJob j = new UIJob(Display.getDefault(),jobName) {
public IStatus runInUIThread(IProgressMonitor monitor) {
if (error) {
Color c = cs.getColor();
cs.setColor(errorColor);
cs.println(msg);
if (e != null) {
PrintStream p = new PrintStream(cs);
e.printStackTrace(p);
p.flush();
}
try { cs.flush(); } catch (Exception ee) {} // ignore
cs.setColor(c);
} else if (newline) {
cs.println(msg);
} else {
cs.print(msg);
}
// Also write it to the log file, if requested.
if (alsoLogInfo || error) {
pluginLog.log(
new Status(error ? Status.ERROR : Status.INFO,
pluginID,
Status.OK, msg, null));
}
return Status.OK_STATUS;
}
};
// FIXME - does this need a scheduling rule?
j.setUser(true);
j.schedule();
} else {
if (error) {
Color c = cs.getColor();
cs.setColor(errorColor);
cs.println(msg);
if (e != null) {
PrintStream p = new PrintStream(cs);
e.printStackTrace(p);
p.flush();
}
try { cs.flush(); } catch (Exception ee) {} // ignore
cs.setColor(c);
} else if (newline) {
cs.println(msg);
} else {
cs.print(msg);
}
// Also write it to the log file, if requested.
if (alsoLogInfo || error) {
pluginLog.log(
new Status(error ? Status.ERROR : Status.INFO,
pluginID,
Status.OK, msg, null));
}
}
}
/** Flushes the console stream */
public void flush() throws IOException {
getConsoleStream().flush();
}
}