/**
* Copyright (C) 2004, 2005, 2006 Aelitis SAS, All rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
* 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 General Public License for more details ( see the LICENSE file ).
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* AELITIS, SAS au capital de 46,603.30 euros,
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*/
package org.gudy.azureus2.ui.swt.views;
import java.io.PrintStream;
import java.text.FieldPosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.*;
import org.gudy.azureus2.core3.config.ParameterListener;
import org.gudy.azureus2.core3.internat.MessageText;
import org.gudy.azureus2.core3.logging.*;
import org.gudy.azureus2.core3.logging.impl.FileLogging;
import org.gudy.azureus2.core3.util.AERunnable;
import org.gudy.azureus2.ui.swt.Messages;
import org.gudy.azureus2.ui.swt.Utils;
import org.gudy.azureus2.ui.swt.mainwindow.Colors;
/**
* @author TuxPaper
*
* @since 2.3.0.5
*/
public class LoggerView extends AbstractIView implements ILogEventListener,
ParameterListener {
//private final static LogIDs LOGID = LogIDs.GUI;
private static final int COLOR_INFO = 0;
private static final int COLOR_WARN = 1;
private static final int COLOR_ERR = 2;
private static Color[] colors = null;
private static final int PREFERRED_LINES = 256;
private static final int MAX_LINES = 1024 + PREFERRED_LINES;
private static final SimpleDateFormat dateFormatter;
private static final FieldPosition formatPos;
private Display display;
private Composite panel;
private StyledText consoleText = null;
private Button buttonAutoScroll = null;
private Object[] filter = null;
// LinkedList is better for removing entries when full
private LinkedList buffer = new LinkedList();
private boolean bPaused = false;
private boolean bRealtime = false;
private boolean bEnabled = false;
private boolean bAutoScroll = true;
private Pattern inclusionFilter;
private Pattern exclusionFilter;
// List of components we don't log.
// Array represents LogTypes (info, warning, error)
private ArrayList[] ignoredComponents = new ArrayList[3];
private boolean stopOnNull = false;
static {
dateFormatter = new SimpleDateFormat("[HH:mm:ss.SSS] ");
formatPos = new FieldPosition(0);
}
public LoggerView() {
this(false);
setEnabled(true);
}
public LoggerView(boolean stopOnNull) {
for (int i = 0; i < ignoredComponents.length; i++) {
ignoredComponents[i] = new ArrayList();
}
this.stopOnNull = stopOnNull;
}
public LoggerView(java.util.List initialList) {
this();
if (initialList != null)
buffer.addAll(initialList);
setEnabled(true);
}
/* (non-Javadoc)
* @see org.gudy.azureus2.ui.swt.IView#initialize(org.eclipse.swt.widgets.Composite)
*/
public void initialize(Composite composite) {
display = composite.getDisplay();
Colors.getInstance().addColorsChangedListener(this);
parameterChanged("Color");
panel = new Composite(composite, SWT.NULL);
GridLayout layout = new GridLayout();
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.verticalSpacing = 2;
layout.numColumns = 2;
panel.setLayout(layout);
GridData gd;
consoleText = new StyledText(panel, SWT.READ_ONLY | SWT.V_SCROLL
| SWT.H_SCROLL);
gd = new GridData(GridData.FILL_BOTH);
gd.horizontalSpan = 2;
consoleText.setLayoutData(gd);
// XXX This doesn't work well, but it's better than nothing
consoleText.addListener(SWT.Resize, new Listener() {
public void handleEvent(Event event) {
GC gc = new GC(consoleText);
int charWidth = gc.getFontMetrics().getAverageCharWidth();
gc.dispose();
int areaWidth = consoleText.getBounds().width;
consoleText.setTabs(areaWidth / 6 / charWidth);
}
});
ScrollBar sb = consoleText.getVerticalBar();
sb.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
bAutoScroll = false;
if (buttonAutoScroll != null && !buttonAutoScroll.isDisposed())
buttonAutoScroll.setSelection(false);
}
});
Composite cLeft = new Composite(panel, SWT.NULL);
layout = new GridLayout();
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.verticalSpacing = 1;
cLeft.setLayout(layout);
gd = new GridData(SWT.TOP, SWT.LEAD, false, false);
cLeft.setLayoutData(gd);
Button buttonPause = new Button(cLeft, SWT.CHECK);
Messages.setLanguageText(buttonPause, "LoggerView.pause");
gd = new GridData();
buttonPause.setLayoutData(gd);
buttonPause.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
if (e.widget == null || !(e.widget instanceof Button))
return;
Button btn = (Button) e.widget;
bPaused = btn.getSelection();
if (!bPaused && buffer != null) {
refresh();
}
}
});
Button buttonRealtime = new Button(cLeft, SWT.CHECK);
Messages.setLanguageText(buttonRealtime, "LoggerView.realtime");
gd = new GridData();
buttonRealtime.setLayoutData(gd);
buttonRealtime.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
if (e.widget == null || !(e.widget instanceof Button))
return;
Button btn = (Button) e.widget;
bRealtime = btn.getSelection();
}
});
buttonAutoScroll = new Button(cLeft, SWT.CHECK);
Messages.setLanguageText(buttonAutoScroll, "LoggerView.autoscroll");
gd = new GridData();
buttonAutoScroll.setLayoutData(gd);
buttonAutoScroll.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
if (e.widget == null || !(e.widget instanceof Button))
return;
Button btn = (Button) e.widget;
bAutoScroll = btn.getSelection();
}
});
buttonAutoScroll.setSelection(true);
Button buttonClear = new Button(cLeft, SWT.PUSH);
Messages.setLanguageText(buttonClear, "LoggerView.clear");
gd = new GridData();
buttonClear.setLayoutData(gd);
buttonClear.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
consoleText.setText("");
}
});
/** FileLogging filter, consisting of a List of types (info, warning, error)
* and a checkbox Table of component IDs.
*/
final String sFilterPrefix = "ConfigView.section.logging.filter";
Group gLogIDs = new Group(panel, SWT.NULL);
Messages.setLanguageText(gLogIDs, "LoggerView.filter");
layout = new GridLayout();
layout.marginHeight = 0;
layout.numColumns = 2;
gLogIDs.setLayout(layout);
gd = new GridData();
gLogIDs.setLayoutData(gd);
Label label = new Label(gLogIDs, SWT.NONE);
Messages.setLanguageText(label, "ConfigView.section.logging.level");
label.setLayoutData(new GridData());
final Label labelCatFilter = new Label(gLogIDs, SWT.NONE);
labelCatFilter.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));
final List listLogTypes = new List(gLogIDs, SWT.BORDER | SWT.SINGLE
| SWT.V_SCROLL);
gd = new GridData(SWT.NULL, SWT.BEGINNING, false, false);
listLogTypes.setLayoutData(gd);
final int[] logTypes = { LogEvent.LT_INFORMATION, LogEvent.LT_WARNING,
LogEvent.LT_ERROR };
for (int i = 0; i < logTypes.length; i++)
listLogTypes.add(MessageText.getString("ConfigView.section.logging.log"
+ i + "type"));
listLogTypes.select(0);
final LogIDs[] logIDs = FileLogging.configurableLOGIDs;
//Arrays.sort(logIDs);
Composite cChecksAndButtons = new Composite(gLogIDs, SWT.NULL);
layout = new GridLayout(2, false);
layout.marginHeight = 0;
layout.marginWidth = 0;
cChecksAndButtons.setLayout(layout);
cChecksAndButtons.setLayoutData(new GridData());
final Composite cChecks = new Composite(cChecksAndButtons, SWT.NULL);
RowLayout rowLayout = new RowLayout(SWT.VERTICAL);
rowLayout.wrap = true;
rowLayout.marginLeft = 0;
rowLayout.marginRight = 0;
rowLayout.marginTop = 0;
rowLayout.marginBottom = 0;
cChecks.setLayout(rowLayout);
SelectionAdapter buttonClickListener = new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
int index = listLogTypes.getSelectionIndex();
if (index < 0 || index >= logTypes.length)
return;
Button item = (Button) e.widget;
if (item.getSelection())
ignoredComponents[index].remove(item.getData("LOGID"));
else
ignoredComponents[index].add(item.getData("LOGID"));
}
};
for (int i = 0; i < logIDs.length; i++) {
Button btn = new Button(cChecks, SWT.CHECK);
btn.setText(MessageText.getString(sFilterPrefix + "." + logIDs[i],
logIDs[i].toString()));
btn.setData("LOGID", logIDs[i]);
btn.addSelectionListener(buttonClickListener);
if (i == 0) {
gd = new GridData(SWT.FILL, SWT.FILL, false, false, 1, 2);
gd.heightHint = (btn.computeSize(SWT.DEFAULT, SWT.DEFAULT).y + 2) * 3;
cChecks.setLayoutData(gd);
}
}
// Update table when list selection changes
listLogTypes.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
int index = listLogTypes.getSelectionIndex();
if (index < 0 || index >= logTypes.length)
return;
labelCatFilter.setText(MessageText.getString(
"ConfigView.section.logging.showLogsFor", listLogTypes
.getSelection()));
Control[] items = cChecks.getChildren();
for (int i = 0; i < items.length; i++) {
if (items[i] instanceof Button) {
LogIDs ID = (LogIDs) items[i].getData("LOGID");
if (ID != null) {
boolean checked = !ignoredComponents[index].contains(ID);
((Button) items[i]).setSelection(checked);
}
}
}
}
});
listLogTypes.notifyListeners(SWT.Selection, null);
Button btn;
btn = new Button(cChecksAndButtons, SWT.PUSH);
gd = new GridData();
btn.setLayoutData(gd);
Messages.setLanguageText(btn, "LoggerView.filter.checkAll");
btn.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
int index = listLogTypes.getSelectionIndex();
Control[] items = cChecks.getChildren();
for (int i = 0; i < items.length; i++) {
if (items[i] instanceof Button) {
LogIDs ID = (LogIDs) items[i].getData("LOGID");
if (ID != null && ignoredComponents[index].contains(ID)) {
((Button) items[i]).setSelection(true);
ignoredComponents[index].remove(ID);
}
}
}
}
});
btn = new Button(cChecksAndButtons, SWT.PUSH);
gd = new GridData();
btn.setLayoutData(gd);
Messages.setLanguageText(btn, "LoggerView.filter.uncheckAll");
btn.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
int index = listLogTypes.getSelectionIndex();
Control[] items = cChecks.getChildren();
for (int i = 0; i < items.length; i++) {
if (items[i] instanceof Button) {
LogIDs ID = (LogIDs) items[i].getData("LOGID");
if (ID != null && !ignoredComponents[index].contains(ID)) {
((Button) items[i]).setSelection(false);
ignoredComponents[index].add(ID);
}
}
}
}
});
Composite cBottom = new Composite(panel, SWT.NONE);
gd = new GridData(SWT.FILL, SWT.FILL, true, false);
gd.horizontalSpan = 2;
cBottom.setLayoutData(gd);
cBottom.setLayout(new GridLayout(2, false));
label = new Label(cBottom, SWT.NONE);
label.setLayoutData(new GridData());
Messages.setLanguageText(label, "LoggerView.includeOnly");
final Text inclText = new Text(cBottom, SWT.BORDER);
gd = new GridData();
gd.widthHint = 200;
inclText.setLayoutData(gd);
inclText.addModifyListener(new ModifyListener()
{
public void modifyText(ModifyEvent e) {
String newExpression = inclText.getText();
if (newExpression.length() == 0)
inclusionFilter = null;
else
{
try
{
inclusionFilter = Pattern.compile(newExpression, Pattern.CASE_INSENSITIVE);
inclText.setBackground(null);
} catch (PatternSyntaxException e1)
{
inclText.setBackground(Colors.colorErrorBG);
}
}
}
});
label = new Label(cBottom, SWT.NONE);
label.setLayoutData(new GridData());
Messages.setLanguageText(label, "LoggerView.excludeAll");
final Text exclText = new Text(cBottom, SWT.BORDER);
gd = new GridData();
gd.widthHint = 200;
exclText.setLayoutData(gd);
exclText.addModifyListener(new ModifyListener()
{
public void modifyText(ModifyEvent e) {
String newExpression = exclText.getText();
if (newExpression.length() == 0)
exclusionFilter = null;
else
{
try
{
exclusionFilter = Pattern.compile(newExpression, Pattern.CASE_INSENSITIVE);
exclText.setBackground(null);
} catch (PatternSyntaxException e1)
{
exclText.setBackground(Colors.colorErrorBG);
}
}
}
});
if (!Logger.isEnabled()) {
consoleText.setText(MessageText.getString("LoggerView.loggingDisabled")
+ "\n");
}
}
/* (non-Javadoc)
* @see org.gudy.azureus2.ui.swt.IView#getComposite()
*/
public Composite getComposite() {
return panel;
}
/* (non-Javadoc)
* @see org.gudy.azureus2.ui.swt.IView#refresh()
*/
public void refresh() {
if (bPaused)
return;
synchronized (buffer) {
if (consoleText == null || consoleText.isDisposed())
return;
for (int i = 0; i < buffer.size(); i++) {
try {
LogEvent event = (LogEvent) buffer.get(i);
int nbLinesBefore = consoleText.getLineCount();
if (nbLinesBefore > MAX_LINES)
{
consoleText.replaceTextRange(0, consoleText.getOffsetAtLine(PREFERRED_LINES), "");
nbLinesBefore = consoleText.getLineCount();
}
final StringBuffer buf = new StringBuffer();
buf.append('\n');
dateFormatter.format(event.timeStamp, buf, formatPos);
buf.append("{").append(event.logID).append("} ");
buf.append(event.text);
if (event.relatedTo != null) {
buf.append("; \t| ");
for (int j = 0; j < event.relatedTo.length; j++) {
Object obj = event.relatedTo[j];
if (j > 0)
buf.append("; ");
if (obj instanceof LogRelation) {
buf.append(((LogRelation) obj).getRelationText());
} else if (obj != null) {
buf.append(obj.getClass().getName()).append(": '").append(
obj.toString()).append("'");
}
}
}
String toAppend = buf.toString();
if((inclusionFilter != null && !inclusionFilter.matcher(toAppend).find()) || (exclusionFilter != null && exclusionFilter.matcher(toAppend).find()))
continue;
consoleText.append(toAppend);
int nbLinesNow = consoleText.getLineCount();
int colorIdx = -1;
if (event.entryType == LogEvent.LT_INFORMATION)
colorIdx = COLOR_INFO;
else if (event.entryType == LogEvent.LT_WARNING)
colorIdx = COLOR_WARN;
else if (event.entryType == LogEvent.LT_ERROR)
colorIdx = COLOR_ERR;
if (colors != null && colorIdx >= 0)
consoleText.setLineBackground(nbLinesBefore, nbLinesNow
- nbLinesBefore, colors[colorIdx]);
} catch (Exception e) {
// don't send it to log, we might be feeding ourselves
PrintStream ps = Logger.getOldStdErr();
if (ps != null) {
ps.println("Error writing event to console:");
e.printStackTrace(ps);
}
}
}
buffer.clear();
if (bAutoScroll)
consoleText.setTopIndex(consoleText.getLineCount());
}
}
/* (non-Javadoc)
* @see org.gudy.azureus2.ui.swt.IView#delete()
*/
public void delete() {
Logger.removeListener(this);
if (panel != null && !panel.isDisposed())
panel.dispose();
Colors.getInstance().removeColorsChangedListener(this);
}
/* (non-Javadoc)
* @see org.gudy.azureus2.ui.swt.IView#getFullTitle()
*/
public String getFullTitle() {
return MessageText.getString("ConsoleView.title.full");
}
public String getData() {
return "ConsoleView.title.short";
}
// @see org.gudy.azureus2.core3.logging.ILogEventListener#log(org.gudy.azureus2.core3.logging.LogEvent)
public synchronized void log(final LogEvent event) {
if (display == null || display.isDisposed())
return;
if (ignoredComponents[logTypeToIndex(event.entryType)].contains(event.logID))
return;
// Always display STDERR messages, as they may relate to the filter
boolean bMatch = (event.logID == LogIDs.STDERR || filter == null);
if (!bMatch && event.relatedTo != null) {
for (int i = 0; !bMatch && i < event.relatedTo.length; i++) {
Object obj = event.relatedTo[i];
if (obj == null)
continue;
for (int j = 0; !bMatch && j < filter.length; j++) {
if (obj instanceof LogRelation) {
//System.err.println(obj.getClass().getSimpleName() + " is Logrelation");
Object newObj = ((LogRelation) obj).queryForClass(filter[j]
.getClass());
if (newObj != null)
obj = newObj;
}
//System.err.println(obj.getClass().getName() + " matches " + filter[j].getClass().getSimpleName() + "?");
if (obj == filter[j])
bMatch = true;
} // for filter
} // for relatedTo
}
if (bMatch) {
synchronized (buffer) {
if (buffer.size() >= 200)
buffer.removeFirst();
buffer.add(event);
}
if (bRealtime && !bPaused) {
Utils.execSWTThread(new AERunnable() {
public void runSupport() {
refresh();
}
});
}
}
}
public void setFilter(Object[] _filter) {
filter = _filter;
clearConsole();
}
private void clearConsole() {
if (display == null || display.isDisposed())
return;
Utils.execSWTThread(new AERunnable() {
public void runSupport() {
consoleText.setText("");
}
});
}
public void setEnabled(boolean on) {
if (bEnabled == on)
return;
bEnabled = on;
if (on)
Logger.addListener(this);
else
Logger.removeListener(this);
}
/* (non-Javadoc)
* @see org.gudy.azureus2.plugins.PluginView#getPluginViewName()
*/
public String getPluginViewName() {
return "Console";
}
// TODO: Support multiple selection
public void dataSourceChanged(Object newDataSource) {
if (newDataSource == null) {
if (stopOnNull) {
setEnabled(false);
return;
}
setFilter(null);
} else if (newDataSource instanceof Object[]) {
setFilter((Object[]) newDataSource);
} else {
setFilter(new Object[] { newDataSource });
}
setEnabled(true);
}
private int logTypeToIndex(int entryType) {
switch (entryType) {
case LogEvent.LT_INFORMATION:
return 0;
case LogEvent.LT_WARNING:
return 1;
case LogEvent.LT_ERROR:
return 2;
}
return 0;
}
private int indexToLogType(int index) {
switch (index) {
case 0:
return LogEvent.LT_INFORMATION;
case 1:
return LogEvent.LT_WARNING;
case 2:
return LogEvent.LT_ERROR;
}
return LogEvent.LT_INFORMATION;
}
public void parameterChanged(String parameterName) {
if (parameterName.startsWith("Color")) {
Utils.execSWTThread(new AERunnable() {
public void runSupport() {
if (display == null || display.isDisposed())
return;
if (colors == null)
colors = new Color[3];
final Color[] newColors = { Colors.blues[Colors.BLUES_MIDLIGHT],
Colors.colorWarning, Colors.red_ConsoleView };
boolean bColorChanged = false;
for (int i = 0; i < newColors.length; i++) {
if (colors[i] == null || colors[i].isDisposed()) {
colors[i] = newColors[i];
bColorChanged = true;
}
}
if (bColorChanged && consoleText != null) {
// remove color
String text = consoleText.getText();
consoleText.setText(text);
}
}
});
}
}
}