/*=============================================================================#
# Copyright (c) 2005-2016 Stephan Wahlbrink (WalWare.de)
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
# Contributors:
# Stephan Wahlbrink - initial API and implementation
#=============================================================================*/
package de.walware.statet.nico.ui.console;
import java.io.IOException;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IDebugEventSetListener;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.IStreamListener;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.IStreamMonitor;
import org.eclipse.jface.resource.FontRegistry;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsoleView;
import org.eclipse.ui.console.TextConsole;
import org.eclipse.ui.part.IPageBookViewPage;
import de.walware.ecommons.preferences.PreferencesUtil;
import de.walware.ecommons.preferences.SettingsChangeNotifier;
import de.walware.ecommons.ts.ITool;
import de.walware.ecommons.ui.util.UIAccess;
import de.walware.statet.nico.core.runtime.SubmitType;
import de.walware.statet.nico.core.runtime.ToolProcess;
import de.walware.statet.nico.core.runtime.ToolStreamMonitor;
import de.walware.statet.nico.internal.ui.NicoUIPlugin;
import de.walware.statet.nico.internal.ui.console.NIConsolePartitioner;
import de.walware.statet.nico.internal.ui.preferences.ConsolePreferences;
import de.walware.statet.nico.ui.NicoUIPreferences;
import de.walware.statet.nico.ui.NicoUITools;
/**
* A console to interact with controller using command-line-based interface.
*/
public abstract class NIConsole extends TextConsole implements IAdaptable {
public static final String NICONSOLE_TYPE = "de.walware.statet.nico.console"; //$NON-NLS-1$
public static final String ADJUST_OUTPUT_WIDTH_COMMAND_ID = "de.walware.statet.nico.commands.AdjustOutputWidth"; //$NON-NLS-1$
private static boolean gFontInitialized;
private class SettingsListener implements SettingsChangeNotifier.ChangeListener, IPropertyChangeListener {
@Override
public void settingsChanged(final Set<String> groupIds) {
if (groupIds.contains(ConsolePreferences.GROUP_ID)) {
updateSettings();
}
if (groupIds.contains(ConsolePreferences.OUTPUT_TEXTSTYLE_GROUP_ID)) {
final NIConsoleColorAdapter adapter = fAdapter;
if (adapter != null) {
adapter.updateSettings();
}
}
}
@Override
public void propertyChange(final PropertyChangeEvent event) {
if (getSymbolicFontName().equals(event.getProperty()) ) {
setFont(null);
}
}
}
private final NIConsolePartitioner fPartitioner;
private final Map<String, NIConsoleOutputStream> fStreams= new HashMap<>();
private boolean fStreamsClosed;
private final ToolProcess fProcess;
private NIConsoleColorAdapter fAdapter;
private IDebugEventSetListener fDebugListener;
private final SettingsListener fSettingsListener = new SettingsListener();
private int fCurrentWatermark;
/**
* Constructs a new console.
*
* @param name console name
*/
public NIConsole(final ToolProcess process, final NIConsoleColorAdapter adapter) {
super(process.getAttribute(IProcess.ATTR_PROCESS_LABEL),
NICONSOLE_TYPE,
NicoUITools.getImageDescriptor(process),
true);
fProcess = process;
fAdapter = adapter;
fPartitioner = new NIConsolePartitioner(this, fAdapter.getStreamIds());
fPartitioner.connect(getDocument());
if (!gFontInitialized) {
UIAccess.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
setFont(null);
gFontInitialized = true;
}
});
}
else {
setFont(null);
}
PreferencesUtil.getSettingsChangeNotifier().addChangeListener(fSettingsListener);
updateWatermarks();
fStreamsClosed = fProcess.isTerminated();
fAdapter.connect(process, this);
fDebugListener = new IDebugEventSetListener() {
@Override
public void handleDebugEvents(final DebugEvent[] events) {
ITER_EVENTS: for (final DebugEvent event : events) {
if (event.getSource() == fProcess) {
switch (event.getKind()) {
case DebugEvent.CHANGE:
final Object obj = event.getData();
if (obj != null && obj instanceof String[]) {
final String[] attrChange = (String[]) obj;
if (attrChange.length == 3 && IProcess.ATTR_PROCESS_LABEL.equals(attrChange[0])) {
runSetName(attrChange[2]);
}
}
continue ITER_EVENTS;
case DebugEvent.TERMINATE:
disconnect();
continue ITER_EVENTS;
}
}
}
}
private void runSetName(final String name) {
UIAccess.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
setName(name);
// ConsolePlugin.getDefault().getConsoleManager().warnOfContentChange(NIConsole.this);
ConsolePlugin.getDefault().getConsoleManager().refresh(NIConsole.this);
}
});
}
};
DebugPlugin.getDefault().addDebugEventListener(fDebugListener);
}
protected void updateSettings() {
updateWatermarks();
}
protected void updateWatermarks() {
final boolean limitBufferSize = true;
if (limitBufferSize) {
int lowWater = PreferencesUtil.getInstancePrefs().getPreferenceValue(NicoUIPreferences.OUTPUT_CHARLIMIT_PREF);
if (lowWater < 100000) {
lowWater = 100000;
}
if (lowWater == fCurrentWatermark) {
return;
}
final int highWater = lowWater + 10000;
fPartitioner.setWaterMarks(lowWater, highWater);
}
else {
fPartitioner.setWaterMarks(-1, -1);
}
}
protected String getSymbolicFontName() {
return JFaceResources.TEXT_FONT;
}
@Override
public void setFont(Font newFont) {
if (newFont == null) {
JFaceResources.getFont(getSymbolicFontName()).getFontData()[0].getName();
newFont = JFaceResources.getFont(getSymbolicFontName());
}
super.setFont(newFont);
}
@Override
protected void init() {
super.init();
JFaceResources.getFontRegistry().addListener(fSettingsListener);
}
@Override
public void clearConsole() {
if (fPartitioner != null) {
fPartitioner.clearBuffer();
}
}
@Override
protected void dispose() {
super.dispose();
final DebugPlugin debugPlugin = DebugPlugin.getDefault();
if (debugPlugin != null) {
debugPlugin.removeDebugEventListener(fDebugListener);
}
fDebugListener = null;
final SettingsChangeNotifier changeNotifier = PreferencesUtil.getSettingsChangeNotifier();
if (changeNotifier != null) {
changeNotifier.removeChangeListener(fSettingsListener);
}
final FontRegistry fontRegistry = JFaceResources.getFontRegistry();
if (fontRegistry != null) {
fontRegistry.removeListener(fSettingsListener);
}
disconnect();
}
@Override
public abstract IPageBookViewPage createPage(IConsoleView view);
@Override
protected NIConsolePartitioner getPartitioner() {
return fPartitioner;
}
public void connect(final ToolStreamMonitor streamMonitor, final String streamId,
final EnumSet<SubmitType> filter) {
synchronized (fStreams) {
if (fStreamsClosed) {
return;
}
NIConsoleOutputStream stream = fStreams.get(streamId);
if (stream == null) {
stream = new NIConsoleOutputStream(this, streamId);
fStreams.put(streamId, stream);
}
final NIConsoleOutputStream out = stream;
streamMonitor.addListener(new IStreamListener() {
@Override
public void streamAppended(final String text, final IStreamMonitor monitor) {
try {
out.write(text);
}
catch (final IOException e) {
NicoUIPlugin.logError(NicoUIPlugin.INTERNAL_ERROR, "Error of unexpected type occured, when writing to console stream.", e); //$NON-NLS-1$
}
}
}, filter);
}
}
public NIConsoleOutputStream getStream(final String streamId) {
synchronized (fStreams) {
return fStreams.get(streamId);
}
}
private void disconnect() {
synchronized (fStreams) {
if (fStreamsClosed) {
return;
}
for (final NIConsoleOutputStream stream : fStreams.values()) {
stream.close();
}
fStreamsClosed = true;
fPartitioner.finish();
fAdapter.disconnect();
fAdapter = null;
}
}
public final ToolProcess getProcess() {
return fProcess;
}
@Override
public Object getAdapter(final Class required) {
if (ITool.class.equals(required)) {
return fProcess;
}
if(ILaunchConfiguration.class.equals(required)) {
final ILaunch launch = getProcess().getLaunch();
if(launch != null) {
return launch.getLaunchConfiguration();
}
return null;
}
return null;
}
}