blob: f1505026058e7bd7ab25eb160dd6eefd2f235129 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2005, 2019 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.nico.core.runtime;
import static org.eclipse.statet.nico.core.runtime.IToolEventHandler.REPORT_STATUS_DATA_KEY;
import static org.eclipse.statet.nico.core.runtime.IToolEventHandler.REPORT_STATUS_EVENT_ID;
import static org.eclipse.statet.nico.core.runtime.IToolEventHandler.SCHEDULE_QUIT_EVENT_ID;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.model.IThread;
import org.eclipse.osgi.util.NLS;
import org.eclipse.statet.jcommons.collections.CopyOnWriteIdentityListSet;
import org.eclipse.statet.jcommons.collections.CopyOnWriteList;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.collections.ImIdentityList;
import org.eclipse.statet.jcommons.collections.ImList;
import org.eclipse.statet.jcommons.lang.Disposable;
import org.eclipse.statet.jcommons.lang.ObjectUtils;
import org.eclipse.statet.jcommons.lang.ObjectUtils.ToStringBuilder;
import org.eclipse.statet.jcommons.status.CancelStatus;
import org.eclipse.statet.jcommons.status.ErrorStatus;
import org.eclipse.statet.jcommons.status.InfoStatus;
import org.eclipse.statet.jcommons.status.NullProgressMonitor;
import org.eclipse.statet.jcommons.status.ProgressMonitor;
import org.eclipse.statet.jcommons.status.Status;
import org.eclipse.statet.jcommons.status.StatusException;
import org.eclipse.statet.jcommons.ts.core.SystemRunnable;
import org.eclipse.statet.jcommons.ts.core.Tool;
import org.eclipse.statet.jcommons.ts.core.ToolCommandHandler;
import org.eclipse.statet.jcommons.ts.core.ToolRunnable;
import org.eclipse.statet.jcommons.ts.core.ToolService;
import org.eclipse.statet.ecommons.runtime.core.util.StatusUtils;
import org.eclipse.statet.internal.nico.core.Messages;
import org.eclipse.statet.internal.nico.core.NicoCorePlugin;
import org.eclipse.statet.internal.nico.core.RunnableProgressData;
import org.eclipse.statet.nico.core.NicoCore;
import org.eclipse.statet.nico.core.NicoCoreMessages;
import org.eclipse.statet.nico.core.runtime.Queue.Section;
/**
* Controller for a long running tight integrated tool.
* <p>
* Usage: This class is intend to be subclass. Subclasses are responsible for the
* life cycle of the tool (<code>startTool()</code>, <code>terminateTool()</code>.
* Subclasses should provide an interface which can be used by IToolRunnables
* to access the features of the tool. E.g. provide an abstract implementation of
* IToolRunnable with the necessary methods (in protected scope).</p>
*
* Methods with protected visibility and marked with an L at the end their names must be called only
* within the lifecycle thread.
*/
public abstract class ToolController implements ConsoleService {
private static final boolean DEBUG_LOG_STATE= Boolean.parseBoolean(
Platform.getDebugOption("org.eclipse.statet.nico/debug/ToolController/logState") ); //$NON-NLS-1$
private static final int STEP_BEGIN= (DebugEvent.STEP_INTO | DebugEvent.STEP_OVER | DebugEvent.STEP_RETURN);
/**
* Listens for changes of the status of a controller.
*
* Use this only if it's really necessary, otherwise listen to
* debug events of the ToolProcess.
*/
public static interface IToolStatusListener {
/**
* Should be fast!
*
* This method is called in the tool lifecycle thread
* and blocks the queue.
*
* @param oldStatus
* @param newStatus
* @param eventCollection a collection, you can add you own debug events to.
*/
void controllerStatusChanged(ToolStatus oldStatus, ToolStatus newStatus, List<DebugEvent> eventCollection);
// void controllerBusyChanged(boolean isBusy, final List<DebugEvent> eventCollection);
}
private static IProgressMonitor fgProgressMonitorDummy= new org.eclipse.core.runtime.NullProgressMonitor();
protected abstract class ControllerSystemRunnable implements SystemRunnable {
private final String fTypeId;
private final String fLabel;
public ControllerSystemRunnable(final String typeId, final String label) {
this.fTypeId= typeId;
this.fLabel= label;
}
@Override
public String getTypeId() {
return this.fTypeId;
}
@Override
public String getLabel() {
return this.fLabel;
}
@Override
public boolean canRunIn(final Tool tool) {
return (tool == getTool());
}
@Override
public boolean changed(final int event, final Tool tool) {
if (event == MOVING_FROM) {
return false;
}
return true;
}
}
/**
* Default implementation of a runnable which can be used for
* {@link ToolController#createCommandRunnable(String, SubmitType)}.
*
* Usage: This class is intend to be subclassed.
*/
public abstract static class ConsoleCommandRunnable implements ConsoleRunnable {
public static final String TYPE_ID= "common/console/input"; //$NON-NLS-1$
protected final String fText;
protected String fLabel;
protected final SubmitType fType;
protected ConsoleCommandRunnable(final String text, final SubmitType type) {
assert (text != null);
assert (type != null);
this.fText= text;
this.fType= type;
}
@Override
public String getTypeId() {
return TYPE_ID;
}
@Override
public SubmitType getSubmitType() {
return this.fType;
}
public String getCommand() {
return this.fText;
}
@Override
public String getLabel() {
if (this.fLabel == null) {
this.fLabel= this.fText.trim();
}
return this.fLabel;
}
@Override
public boolean changed(final int event, final Tool process) {
return true;
}
@Override
public void run(final ToolService service,
final ProgressMonitor m) throws StatusException {
((ConsoleService) service).submitToConsole(this.fText, m);
}
}
protected class StartRunnable implements ConsoleRunnable {
public StartRunnable() {
}
@Override
public String getTypeId() {
return START_TYPE_ID;
}
@Override
public boolean canRunIn(final Tool tool) {
return (tool == getTool());
}
@Override
public SubmitType getSubmitType() {
return SubmitType.CONSOLE;
}
@Override
public String getLabel() {
return Messages.ToolController_CommonStartTask_label;
}
@Override
public boolean changed(final int event, final Tool process) {
if ((event & MASK_EVENT_GROUP) == REMOVING_EVENT_GROUP) {
return false;
}
return true;
}
@Override
public void run(final ToolService s,
final ProgressMonitor m) throws StatusException {
}
}
private class SuspendedInsertRunnable extends ControllerSystemRunnable implements SystemRunnable {
private final int level;
public SuspendedInsertRunnable(final int level) {
super(SUSPENDED_INSERT_TYPE_ID, "Suspended [" + level + "]");
this.level= level;
}
@Override
public boolean changed(final int event, final Tool process) {
switch (event) {
case REMOVING_FROM:
case MOVING_FROM:
return false;
}
return true;
}
@Override
public void run(final ToolService service,
final ProgressMonitor m) throws StatusException {
}
}
private class SuspendedUpdateRunnable extends ControllerSystemRunnable implements SystemRunnable {
public SuspendedUpdateRunnable() {
super(SUSPENDED_INSERT_TYPE_ID, "Update Debug Context");
}
@Override
public boolean changed(final int event, final Tool tool) {
if (event == MOVING_FROM) {
return false;
}
return true;
}
@Override
public void run(final ToolService service,
final ProgressMonitor m) throws StatusException {
final ImList<SystemRunnable> runnables= ToolController.this.suspendUpdateRunnables.toList();
for (final SystemRunnable runnable : runnables) {
try {
runnable.run(service, m);
}
catch (final StatusException e) {
final Status status= e.getStatus();
if (status != null && (status.getSeverity() == Status.CANCEL || status.getSeverity() <= Status.INFO)) {
// ignore
}
else {
NicoCorePlugin.logError(-1, NLS.bind(
"An error occurred when running suspend task ''{0}''.", //$NON-NLS-1$
runnable.getLabel() ), e);
}
if (isTerminated()) {
return;
}
}
}
}
}
protected abstract class SuspendResumeRunnable extends ControllerSystemRunnable implements ConsoleRunnable {
private int detail;
public SuspendResumeRunnable(final String id, final String label, final int detail) {
super(id, label);
this.detail= detail;
}
@Override
public SubmitType getSubmitType() {
return SubmitType.TOOLS;
}
protected void setDetail(final int detail) {
this.detail= detail;
}
@Override
public boolean changed(final int event, final Tool process) {
switch (event) {
case MOVING_FROM:
return false;
case REMOVING_FROM:
case BEING_ABANDONED:
if (ToolController.this.suspendExitRunnable == this) {
ToolController.this.suspendExitRunnable= null;
}
break;
default:
break;
}
return true;
}
@Override
public void run(final ToolService adapter,
final ProgressMonitor m) throws StatusException {
ToolController.this.suspendExitRunnable= this;
setSuspended(ToolController.this.suspendedLowerLevel, 0, null);
}
protected boolean canExec(final ProgressMonitor m) throws StatusException {
return true;
}
protected abstract void doExec(final ProgressMonitor m) throws StatusException;
protected void submitToConsole(final String print, final String send,
final ProgressMonitor m) throws StatusException {
ToolController.this.currentSubmitType= getSubmitTypeL(this);
try {
ToolController.this.fCurrentInput= print;
doBeforeSubmitL();
}
finally {
ToolController.this.currentSubmitType= ToolController.this.currentRunnable.submitType;
}
if (send != null) {
ToolController.this.fCurrentInput= send;
doSubmitL(m);
}
}
}
protected class QuitRunnable extends SuspendResumeRunnable {
public QuitRunnable() {
super(ToolController.QUIT_TYPE_ID, "Quit", DebugEvent.CLIENT_REQUEST);
}
@Override
public void run(final ToolService service,
final ProgressMonitor m) throws StatusException {
super.run(service, m);
if (!ToolController.this.isTerminated) {
try {
briefAboutToChange();
((ToolController) service).doQuitL(m);
}
catch (final StatusException e) {
if (!ToolController.this.isTerminated) {
handleStatus(new ErrorStatus(NicoCore.BUNDLE_ID,
"An error occured when running quit command.",
e ),
m );
}
}
}
}
@Override
protected void doExec(final ProgressMonitor m) throws StatusException {
}
}
protected static class RunnableData {
private final ToolRunnable runnable;
private final SubmitType submitType;
private final Section queueSection;
public RunnableData(final ToolRunnable runnable, final SubmitType submitType, final Section queueSection) {
this.runnable= runnable;
this.submitType= submitType;
this.queueSection= queueSection;
}
}
public static final String START_TYPE_ID= "common/start"; //$NON-NLS-1$
public static final String QUIT_TYPE_ID= "common/quit"; //$NON-NLS-1$
public static final String SUSPENDED_INSERT_TYPE_ID= "common/debug/suspended.insert"; //$NON-NLS-1$
public static final String SUSPENDED_UPDATE_TYPE_ID= "common/debug/suspended.update"; //$NON-NLS-1$
public static final String SUSPEND_TYPE_ID= "common/debug/suspend"; //$NON-NLS-1$
public static final String RESUME_TYPE_ID= "common/debug/resume"; //$NON-NLS-1$
public static final String STEP_INTO_TYPE_ID= "common/debug/step.in"; //$NON-NLS-1$
public static final String STEP_OVER_TYPE_ID= "common/debug/step.over"; //$NON-NLS-1$
public static final String STEP_RETURN_TYPE_ID= "common/debug/step.return"; //$NON-NLS-1$
public static final int CANCEL_CURRENT= 0x00;
public static final int CANCEL_ALL= 0x01;
public static final int CANCEL_PAUSE= 0x10;
protected static final int SUSPENDED_TOPLEVEL= 0x1;
protected static final int SUSPENDED_DEEPLEVEL= 0x2;
private static final byte REGULAR= 0;
private static final byte HOT_REGULAR= 1;
private static final byte HOT_NESTED= 2;
private ToolStreamProxy streams;
private final ToolProcess process;
private final Queue queue;
private int counter= 0;
private RunnableData currentRunnable;
private SubmitType currentSubmitType;
private final List<SystemRunnable> controllerRunnables= new ArrayList<>();
private SystemRunnable postControllerRunnable;
private RunnableProgressData runnableProgressData;
private Thread controllerThread;
private ToolStatus status= ToolStatus.STARTING;
private ToolStatus statusPrevious;
private final CopyOnWriteIdentityListSet<IToolStatusListener> toolStatusListeners= new CopyOnWriteIdentityListSet<>();
private int internalTask;
private boolean terminateForced;
private volatile boolean isTerminated;
private boolean hotModeDeferred;
private boolean hotModeNested= true;
private byte hotMode= REGULAR;
private final ProgressMonitor hotModeMonitor= new NullProgressMonitor();
private boolean isDebugEnabled;
private int suspendedRequestLevel;
private int loopCurrentLevel; // only within loop
private int suspendedRunLevel; // also when running exit/continue suspended
private int suspendedLowerLevel;
private final CopyOnWriteList<SystemRunnable> suspendUpdateRunnables= new CopyOnWriteList<>();
private SuspendResumeRunnable suspendExitRunnable;
private int suspendEnterDetail;
private Object suspendEnterData;
private int suspendExitDetail;
private ToolWorkspace workspaceData;
private volatile int currentStamp;
private int changeStamp;
private int hotStamp;
private final Map<String, ToolCommandHandler> actionHandlers= new HashMap<>();
// RunnableAdapter proxy for tool lifecycle thread
protected String fCurrentInput;
protected Prompt fCurrentPrompt;
protected Prompt fDefaultPrompt;
protected String fLineSeparator;
private final CopyOnWriteIdentityListSet<Disposable> disposables= new CopyOnWriteIdentityListSet<>();
protected ToolController(final ToolProcess process, final Map<String, Object> connectionInfo) {
this.process= process;
this.process.connectionInfo= connectionInfo;
this.streams= new ToolStreamProxy();
this.queue= process.getQueue();
this.toolStatusListeners.add(this.process);
this.status= ToolStatus.STARTING;
this.runnableProgressData= new RunnableProgressData(Messages.Progress_Starting_label);
this.fCurrentPrompt= Prompt.NONE;
}
protected void setWorksapceData(final ToolWorkspace workspaceData) {
this.workspaceData= workspaceData;
}
public final void addCommandHandler(final String commandId, final ToolCommandHandler handler) {
this.actionHandlers.put(commandId, handler);
}
public final ToolCommandHandler getCommandHandler(final String commandId) {
return this.actionHandlers.get(commandId);
}
/**
* Adds a tool status listener.
*
* @param listener
*/
public final void addToolStatusListener(final IToolStatusListener listener) {
this.toolStatusListeners.add(listener);
}
/**
* Removes the tool status listener.
*
* @param listener
*/
public final void removeToolStatusListener(final IToolStatusListener listener) {
this.toolStatusListeners.remove(listener);
}
protected void setStartupTimestamp(final long timestamp) {
this.process.setStartupTimestamp(timestamp);
}
protected void setStartupWD(final String wd) {
this.process.setStartupWD(wd);
}
protected void addDisposable(final Disposable disposable) {
this.disposables.add(disposable);
}
protected final Queue getQueue() {
return this.queue;
}
public final ToolStatus getStatus() {
synchronized (this.queue) {
return this.status;
}
}
protected final ToolStatus getStatusL() {
return this.status;
}
public final IProgressInfo getProgressInfo() {
return this.runnableProgressData;
}
protected final Thread getControllerThread() {
return this.controllerThread;
}
public final ToolStreamProxy getStreams() {
return this.streams;
}
@Override
public ToolProcess getTool() {
return this.process;
}
/**
* Runs the tool.
*
* This method should be called only in a thread explicit for this tool process.
* The thread exits this method, if the tool is terminated.
*/
public final void run() throws StatusException {
assert (this.status == ToolStatus.STARTING);
try {
final Section queueSection= this.queue.getTopLevelSection();
this.controllerThread= Thread.currentThread();
setCurrentRunnable(createRunnableData(createStartRunnable(), queueSection));
startToolL(this.runnableProgressData.getRoot());
setCurrentRunnable(new RunnableData(null, SubmitType.CONSOLE, queueSection));
synchronized (this.queue) {
loopChangeStatus((this.controllerRunnables.isEmpty()) ?
ToolStatus.STARTED_IDLING : ToolStatus.STARTED_PROCESSING, null);
}
loopTopLevel(queueSection);
}
finally {
synchronized (this.queue) {
if (!this.isTerminated) {
this.isTerminated= true;
}
loopChangeStatus(ToolStatus.TERMINATED, null);
this.queue.notifyAll();
}
clear();
this.controllerThread= null;
}
}
public final int getHotTasksState() {
synchronized (this.queue) {
return this.hotMode;
}
}
protected final boolean isInHotModeL() {
return (this.hotMode != 0);
}
/**
* Returns whether the tool is suspended.
* It is suspended if the tool status is {@link ToolStatus#STARTED_SUSPENDED suspended},
* but also if it is processing or paused within the suspended mode of the tool (e.g.
* evaluations to inspect the objects).
*
* @return <code>true</code> if suspended, otherwise <code>false</code>
*/
public final boolean isSuspended() {
synchronized (this.queue) {
return (this.suspendedRequestLevel > 0 || this.loopCurrentLevel > 0);
}
}
/**
* {@link #isSuspended()}
*/
protected final boolean isSuspendedL() {
return (this.suspendedRequestLevel > 0 || this.loopCurrentLevel > 0);
}
/**
* Returns the current value of the task counter. The counter is increas
* before running a task.
*
* @return the counter value
*/
public int getTaskCounter() {
return this.counter;
}
/**
* Tries to apply "cancel".
*
* The return value signals if the command was applicable and/or
* was succesful (depends on implementation).
*
* @return hint about success.
*/
public final boolean cancelTask(final int options) {
synchronized (this.queue) {
if ((options & CANCEL_ALL) != 0) {
final List<ToolRunnable> list= this.queue.internal_getCurrentList();
list.clear();
}
if ((options & CANCEL_PAUSE) != 0) {
this.queue.pause();
}
this.runnableProgressData.setCanceled(true);
beginInternalTask();
if (this.suspendedRequestLevel > this.loopCurrentLevel) {
setSuspended(this.loopCurrentLevel, 0, null);
this.suspendExitDetail= DebugEvent.RESUME;
}
else if (this.loopCurrentLevel > this.suspendedLowerLevel) {
setSuspended(this.suspendedLowerLevel, 0, null);
this.suspendExitDetail= DebugEvent.RESUME;
}
}
try {
interruptTool();
return true;
}
catch (final UnsupportedOperationException e) {
return false;
}
finally {
synchronized (this.queue) {
scheduleControllerRunnable(createCancelPostRunnable(options));
endInternalTask();
}
}
}
/**
* Requests to terminate the controller/process asynchronously.
*
* Same as ToolProcess.terminate()
* The tool will shutdown (after the next runnable) usually.
*/
public final void scheduleQuit() {
synchronized (this.queue) {
if (this.status == ToolStatus.TERMINATED) {
return;
}
beginInternalTask();
}
// ask handler
boolean schedule= true;
try {
final ToolCommandHandler handler= this.actionHandlers.get(SCHEDULE_QUIT_EVENT_ID);
if (handler != null) {
final Map<String, Object> data= new HashMap<>();
data.put("scheduledQuitRunnables", getQuitRunnables()); //$NON-NLS-1$
final Status status= executeHandler(SCHEDULE_QUIT_EVENT_ID, handler, data, new NullProgressMonitor());
if (status != null && status.getSeverity() > Status.OK) {
schedule= false;
}
}
}
finally {
synchronized (this.queue) {
if (this.status != ToolStatus.TERMINATED) {
if (schedule) {
final ToolRunnable runnable= createQuitRunnable();
this.queue.add(runnable);
}
}
endInternalTask();
}
}
}
protected void setTracks(final List<? extends ITrack> tracks) {
this.process.setTracks(tracks);
}
protected final void beginInternalTask() {
this.internalTask++;
}
protected final void endInternalTask() {
this.internalTask--;
if (this.controllerRunnables.size() > 0 || this.internalTask == 0) {
this.queue.notifyAll();
}
}
protected abstract ToolRunnable createStartRunnable();
/**
* Creates a runnable to which can quit the tool.
* The type should be QUIT_TYPE_ID.
* @return
*/
protected QuitRunnable createQuitRunnable() {
return new QuitRunnable();
}
protected SystemRunnable createCancelPostRunnable(final int options) {
return null;
}
/**
* Cancels requests to terminate the controller.
*/
public final void cancelQuit() {
synchronized(this.queue) {
this.queue.remove(getQuitRunnables());
if (this.status == ToolStatus.TERMINATED) {
return;
}
}
// cancel task should not be synch
final ToolRunnable current= this.currentRunnable.runnable;
if (current != null && current.getTypeId() == QUIT_TYPE_ID) {
cancelTask(0);
}
}
private final List<ToolRunnable> getQuitRunnables() {
final ToolRunnable current;
final List<ToolRunnable> waiting;
synchronized (this.queue) {
current= this.currentRunnable.runnable;
waiting= this.queue.internal_getCurrentList();
}
final List<ToolRunnable> quitRunnables= new ArrayList<>();
if (current != null && current.getTypeId() == QUIT_TYPE_ID) {
quitRunnables.add(current);
}
for (final ToolRunnable runnable : waiting) {
if (runnable.getTypeId() == QUIT_TYPE_ID) {
quitRunnables.add(runnable);
}
}
return quitRunnables;
}
public final void kill(final ProgressMonitor m) throws StatusException {
final Thread thread= getControllerThread();
killTool(m);
if (thread != null) {
for (int i= 0; i < 3; i++) {
if (isTerminated()) {
return;
}
synchronized (this.queue) {
this.queue.notifyAll();
try {
this.queue.wait(10);
}
catch (final Exception e) {}
}
thread.interrupt();
}
}
if (!isTerminated()) {
markAsTerminated();
}
}
/**
* Should be only called inside synchronized(fQueue) blocks.
*
* @param newStatus
*/
private final void loopChangeStatus(final ToolStatus newStatus, RunnableProgressData newMonitor) {
if (this.status != newStatus && newMonitor == null) {
newMonitor= new RunnableProgressData(newStatus.getMarkedLabel());
}
// update progress info
if (newMonitor != null) {
this.runnableProgressData= newMonitor;
}
if (newStatus == this.status) {
if (newStatus == ToolStatus.STARTED_PROCESSING && this.loopCurrentLevel > 0) {
// for changed resume detail
final ImIdentityList<IToolStatusListener> listeners= this.toolStatusListeners.toList();
final List<DebugEvent> eventList= this.queue.internal_getEventList();
for (final IToolStatusListener listener : listeners) {
if (listener instanceof IThread) {
listener.controllerStatusChanged(this.statusPrevious, newStatus, eventList);
}
}
}
else {
return;
}
}
else {
if (newStatus == ToolStatus.STARTED_SUSPENDED) {
this.suspendExitDetail= DebugEvent.UNSPECIFIED;
}
if (newStatus == ToolStatus.STARTED_PROCESSING
&& (this.status != ToolStatus.STARTED_PAUSED || this.statusPrevious != ToolStatus.STARTED_PROCESSING)) {
this.queue.internal_resetOnIdle();
}
this.queue.internal_onStatusChanged(newStatus);
this.statusPrevious= this.status;
this.status= newStatus;
final ImIdentityList<IToolStatusListener> listeners= this.toolStatusListeners.toList();
final List<DebugEvent> eventList= this.queue.internal_getEventList();
for (final IToolStatusListener listener : listeners) {
listener.controllerStatusChanged(this.statusPrevious, newStatus, eventList);
}
}
if (DEBUG_LOG_STATE) {
logEvents("loopChangeStatus", newStatus); //$NON-NLS-1$
}
this.queue.internal_fireEvents();
}
// protected final void loopBusyChanged(final boolean isBusy) {
// final IToolStatusListener[] listeners= getToolStatusListeners();
// for (int i= 0; i < listeners.length; i++) {
// listeners[i].controllerBusyChanged(isBusy, fEventCollector);
// }
// final DebugPlugin manager= DebugPlugin.getDefault();
// if (manager != null && !fEventCollector.isEmpty()) {
// manager.fireDebugEventSet(fEventCollector.toArray(new DebugEvent[fEventCollector.size()]));
// }
// fEventCollector.clear();
// }
// public final boolean isStarted() {
// switch (fStatus) {
// case PROCESSING_STATE:
// case IDLING_STATE:
// case PAUSED_STATE:
// return true;
// default:
// return false;
// }
// }
//
// public final boolean isTerminated() {
// return (fStatus == ToolStatus.TERMINATED);
// }
/**
* Only for internal short tasks.
*/
protected final void scheduleControllerRunnable(final SystemRunnable runnable) {
synchronized (this.queue) {
if (!this.controllerRunnables.contains(runnable)) {
this.controllerRunnables.add(runnable);
}
if (this.status != ToolStatus.STARTED_PROCESSING) {
this.queue.notifyAll();
}
}
}
protected final void addPostControllerRunnable(final SystemRunnable runnable) {
this.postControllerRunnable= runnable;
}
protected final void removePostControllerRunnable(final ToolRunnable runnable) {
if (this.postControllerRunnable == runnable) {
this.postControllerRunnable= null;
}
}
/**
* Version for one single text line.
* @see #submit(List, SubmitType)
*
* @param text a single text line.
* @param type type of this submittal.
* @return <code>true</code>, if adding commands to queue was successful,
* otherwise <code>false</code>.
*/
public final Status submit(final String text, final SubmitType type) {
return submit(Collections.singletonList(text), type);
}
/**
* Submits one or multiple text lines to the tool.
* The texts will be treated as usual commands with console output.
*
* @param text array with text lines.
* @param type type of this submittal.
* @param monitor a monitor for cancel, will not be changed.
* @return <code>true</code>, if adding commands to queue was successful,
* otherwise <code>false</code>.
*/
public final Status submit(final List<String> text, final SubmitType type,
final IProgressMonitor monitor) {
try {
monitor.beginTask(NicoCoreMessages.SubmitTask_label, 2);
assert (text != null);
final ToolRunnable[] runs= new ToolRunnable[text.size()];
for (int i= 0; i < text.size(); i++) {
runs[i]= createCommandRunnable(text.get(i), type);
}
if (monitor.isCanceled()) {
return new CancelStatus(NicoCore.BUNDLE_ID,
Messages.ToolController_SubmitCancelled_message, null );
}
monitor.worked(1);
return this.queue.add(ImCollections.newList(runs));
}
finally {
monitor.done();
}
}
/**
* Submits one or multiple text lines to the tool.
* The texts will be treated as usual commands with console output.
*
* @param text array with text lines.
* @param type type of this submittal.
* @return <code>true</code>, if adding commands to queue was successful,
* otherwise <code>false</code>.
*/
public final Status submit(final List<String> text, final SubmitType type) {
return submit(text, type, fgProgressMonitorDummy);
}
/**
* Implement this method to create a runnable for text commands
* (e.g. from console or editor).
*
* The runnable should commit this commands to the tool
* and print command and results to the console.
* Default implementations creates a {@link ConsoleCommandRunnable}.
*
* @param command text command
* @param type type of this submission
* @return runnable for this command
*/
public abstract ToolRunnable createCommandRunnable(final String command, final SubmitType type);
private final void loopTopLevel(final Section queueSection) {
if (this.hotModeDeferred) {
this.hotModeDeferred= false;
scheduleHotMode();
}
boolean enterSuspended= false;
while (true) {
if (enterSuspended) {
enterSuspended= false;
runSuspendedLoopL(SUSPENDED_TOPLEVEL);
}
else {
loopRunTask(queueSection);
}
synchronized (this.queue) { // if interrupted run loop, all states are checked
this.queue.internal_check();
if (this.internalTask > 0) {
try {
this.queue.wait();
}
catch (final InterruptedException e) {}
continue;
}
if (this.isTerminated) {
this.process.setExitValue(finishToolL());
loopChangeStatus(ToolStatus.TERMINATED, null);
return;
}
if (this.controllerRunnables.size() > 0) {
continue;
}
if (this.suspendedRequestLevel > 0) {
enterSuspended= true;
continue;
}
if (this.queue.internal_isPauseRequested()) {
loopChangeStatus(ToolStatus.STARTED_PAUSED, null);
try {
this.queue.wait();
}
catch (final InterruptedException e) {}
continue;
}
if (this.queue.internal_next() < 0) {
loopChangeStatus(ToolStatus.STARTED_IDLING, null);
try {
this.queue.wait();
}
catch (final InterruptedException e) {}
continue;
}
}
}
}
private final void loopSuspended(final int level, final Section queueSection) {
boolean enterSuspended= false;
while (true) {
if (enterSuspended) {
enterSuspended= false;
runSuspendedLoopL(SUSPENDED_TOPLEVEL);
}
else {
loopRunTask(queueSection);
}
synchronized (this.queue) { // if interrupted run loop, all states are checked
this.queue.internal_check();
if (this.internalTask > 0) {
try {
this.queue.wait();
}
catch (final InterruptedException e) {}
continue;
}
if (this.isTerminated) {
return;
}
if (this.suspendedRequestLevel < level) {
return;
}
if (this.controllerRunnables.size() > 0) {
continue;
}
if (this.suspendedRequestLevel > level) {
enterSuspended= true;
continue;
}
if (this.queue.internal_isPauseRequested()) {
loopChangeStatus(ToolStatus.STARTED_PAUSED, null);
try {
this.queue.wait();
}
catch (final InterruptedException e) {}
continue;
}
if (this.queue.internal_next() < 0) {
loopChangeStatus(ToolStatus.STARTED_SUSPENDED, null);
try {
this.queue.wait();
}
catch (final InterruptedException e) {}
continue;
}
}
}
}
private final void loopRunTask(final Section queueSection) {
while (true) {
final int type;
final RunnableData savedCurrentRunnable= this.currentRunnable;
final ToolRunnable runnable;
synchronized (this.queue) {
if (this.controllerRunnables.size() > 0) {
type= Queue.RUN_CONTROL;
runnable= this.controllerRunnables.remove(0);
}
else if (this.loopCurrentLevel != this.suspendedRequestLevel || this.isTerminated
|| this.internalTask > 0 || this.queue.internal_isPauseRequested()) {
return;
}
else {
type= this.queue.internal_next();
switch (type) {
case Queue.RUN_HOT:
runnable= null;
break;
case Queue.RUN_OTHER:
case Queue.RUN_DEFAULT:
runnable= this.queue.internal_poll();
break;
default:
return;
}
}
if (type != Queue.RUN_HOT) {
if (this.loopCurrentLevel > 0) {
if (type != Queue.RUN_CONTROL
&& (runnable instanceof ConsoleCommandRunnable)
&& !runConsoleCommandInSuspend(((ConsoleCommandRunnable) runnable).fText) ) {
try {
this.queue.internal_onFinished(runnable, ToolRunnable.FINISHING_CANCEL);
}
finally {
setCurrentRunnable(savedCurrentRunnable);
}
return;
}
if (runnable instanceof ToolController.SuspendResumeRunnable) {
this.suspendExitDetail= ((ToolController.SuspendResumeRunnable) runnable).detail;
}
else {
final int detail= getDebugResumeDetailL(runnable);
if (this.status == ToolStatus.STARTED_SUSPENDED
|| getDebugResumeDetailPriority(detail) >= getDebugResumeDetailPriority(this.suspendExitDetail)) {
this.suspendExitDetail= detail;
}
}
}
setCurrentRunnable(createRunnableData(runnable, queueSection));
loopChangeStatus(ToolStatus.STARTED_PROCESSING,
new RunnableProgressData(runnable));
}
}
ProgressMonitor runnableProgressMonitor;
switch (type) {
case Queue.RUN_CONTROL:
runnableProgressMonitor= this.runnableProgressData.getRoot();
try {
runnable.run(this, runnableProgressMonitor);
safeRunnableChanged(runnable, ToolRunnable.FINISHING_OK);
continue;
}
catch (final Throwable e) {
final Status status= (e instanceof StatusException) ? ((StatusException) e).getStatus() : null;
if (status != null && (status.getSeverity() == Status.CANCEL || status.getSeverity() <= Status.INFO)) {
safeRunnableChanged(runnable, ToolRunnable.FINISHING_CANCEL);
// ignore
}
else {
NicoCorePlugin.logError(-1, NLS.bind(
"An Error occurred when running internal controller task ''{0}''.", //$NON-NLS-1$
runnable.getLabel() ), e);
safeRunnableChanged(runnable, ToolRunnable.FINISHING_ERROR);
}
if (!isToolAlive()) {
markAsTerminated();
}
return;
}
finally {
setCurrentRunnable(savedCurrentRunnable);
// runnableProgressMonitor.setWorkRemaining(0);
}
case Queue.RUN_HOT:
try {
this.hotModeNested= false;
if (!initilizeHotMode()) {
if (!isToolAlive()) {
markAsTerminated();
}
}
continue;
}
finally {
this.hotModeNested= true;
}
case Queue.RUN_OTHER:
case Queue.RUN_DEFAULT:
runnableProgressMonitor= this.runnableProgressData.getRoot();
try {
this.counter++;
runnableProgressMonitor.setWorkRemaining(100);
runnable.run(this, runnableProgressMonitor.newSubMonitor(99));
runnableProgressMonitor.setWorkRemaining(1);
onTaskFinished(this.currentRunnable, ToolRunnable.FINISHING_OK,
runnableProgressMonitor );
continue;
}
catch (final Throwable e) {
runnableProgressMonitor.setWorkRemaining(1);
Status status= (e instanceof StatusException) ? ((StatusException) e).getStatus() : null;
if (status != null && (
status.getSeverity() == Status.CANCEL || status.getSeverity() <= Status.INFO)) {
onTaskFinished(this.currentRunnable, ToolRunnable.FINISHING_CANCEL,
runnableProgressMonitor );
}
else {
onTaskFinished(this.currentRunnable, ToolRunnable.FINISHING_ERROR,
runnableProgressMonitor );
status= new ErrorStatus(NicoCore.BUNDLE_ID, NicoCorePlugin.EXTERNAL_ERROR,
String.format("Failed to complete '%1$s'.", runnable.getLabel()),
e );
if (type == Queue.RUN_DEFAULT) {
handleStatus(status, runnableProgressMonitor);
}
else {
logRunnableStatus(status, this.currentRunnable);
}
}
if (!isToolAlive()) {
markAsTerminated();
}
return;
}
finally {
runnableProgressMonitor.setWorkRemaining(0);
if (this.postControllerRunnable != null) {
synchronized (this.queue) {
this.controllerRunnables.remove(this.postControllerRunnable);
this.controllerRunnables.add(this.postControllerRunnable);
}
}
setCurrentRunnable(savedCurrentRunnable);
}
}
}
}
/** Only called for regular tasks */
protected void onTaskFinished(final RunnableData runnableData, final int event,
final ProgressMonitor m) {
this.queue.internal_onFinished(runnableData.runnable, event);
safeRunnableChanged(runnableData.runnable, event);
}
private void safeRunnableChanged(final ToolRunnable runnable, final int event) {
try {
runnable.changed(event, this.process);
}
catch (final Throwable e) {
NicoCorePlugin.logError(NicoCorePlugin.EXTERNAL_ERROR,
NLS.bind(Messages.ToolRunnable_error_RuntimeError_message,
this.process.getLabel(Tool.LONG_LABEL),
runnable.getLabel() ),
e );
}
}
protected final void scheduleHotMode() {
final ToolStatus status;
synchronized (this) {
status= this.status;
}
switch (status) {
case TERMINATED:
return;
case STARTING:
this.hotModeDeferred= true;
return;
default:
requestHotMode((Thread.currentThread() != this.controllerThread));
return;
}
}
protected void requestHotMode(final boolean async) {
}
protected boolean initilizeHotMode() {
return true;
}
private final ToolRunnable pollHotRunnable() {
ToolRunnable runnable= null;
if (!this.isTerminated) {
runnable= this.queue.internal_pollHot();
if (runnable == null && !this.hotModeNested) {
try {
this.queue.wait(100);
}
catch (final InterruptedException e) {
}
if (!this.isTerminated) {
runnable= this.queue.internal_pollHot();
}
}
}
return runnable;
}
protected final void runHotModeLoop() {
while (true) {
final ToolRunnable runnable;
synchronized (this.queue) {
runnable= pollHotRunnable();
if (runnable == null) {
if (this.hotMode != 0) {
onHotModeExit(this.hotModeMonitor);
this.hotMode= 0;
this.currentSubmitType= this.currentRunnable.submitType;
}
return;
}
if (this.hotMode == 0) {
this.hotMode= (this.hotModeNested) ? HOT_NESTED : HOT_REGULAR;
this.currentSubmitType= SubmitType.OTHER;
onHotModeEnter(this.hotModeMonitor);
}
this.hotModeMonitor.setCanceled(false);
}
try {
runnable.run(this, this.hotModeMonitor);
safeRunnableChanged(runnable, ToolRunnable.FINISHING_OK);
continue;
}
catch (final Throwable e) {
final Status status= (e instanceof StatusException) ? ((StatusException) e).getStatus() : null;
if (status != null && (status.getSeverity() == Status.CANCEL || status.getSeverity() <= Status.INFO)) {
safeRunnableChanged(runnable, ToolRunnable.FINISHING_CANCEL);
// ignore
}
else {
safeRunnableChanged(runnable, ToolRunnable.FINISHING_ERROR);
NicoCorePlugin.logError(-1, "An Error occurred when running hot task.", e); // //$NON-NLS-1$
}
if (!isToolAlive()) {
markAsTerminated();
}
}
}
}
protected void onHotModeEnter(final ProgressMonitor m) {
if (this.hotMode > HOT_REGULAR) {
enableHotStamp();
}
}
protected void onHotModeExit(final ProgressMonitor m) {
disableHotStamp();
}
public final boolean isDebugEnabled() {
return this.isDebugEnabled;
}
protected void setDebugEnabled(final boolean enabled) {
this.isDebugEnabled= enabled;
}
protected int getDebugResumeDetailL(final ToolRunnable runnable) {
switch (getSubmitTypeL(runnable)) {
case CONSOLE:
case EDITOR:
return DebugEvent.UNSPECIFIED;
case OTHER:
return DebugEvent.EVALUATION_IMPLICIT;
default:
return DebugEvent.EVALUATION;
}
}
private int getDebugResumeDetailPriority(final int detail) {
switch (detail) {
case DebugEvent.EVALUATION_IMPLICIT:
return 1;
case DebugEvent.EVALUATION:
return 2;
case DebugEvent.STEP_INTO:
case DebugEvent.STEP_OVER:
case DebugEvent.STEP_RETURN:
return 3;
case DebugEvent.UNSPECIFIED:
default:
return 4;
}
}
protected int setSuspended(final int level, final int enterDetail, final Object enterData) {
this.suspendedRequestLevel= level;
if (enterDetail == 0
&& (this.suspendExitDetail & STEP_BEGIN) != 0) {
this.suspendEnterDetail= DebugEvent.STEP_END;
this.suspendEnterData= null;
}
else {
this.suspendEnterDetail= enterDetail;
this.suspendEnterData= enterData;
}
return (level - this.suspendedRunLevel);
}
public void addSuspendUpdateRunnable(final SystemRunnable runnable) {
this.suspendUpdateRunnables.add(runnable);
}
protected boolean runConsoleCommandInSuspend(final String input) {
return true;
}
protected void scheduleSuspendExitRunnable(final SuspendResumeRunnable runnable) throws StatusException {
synchronized (this.queue) {
if (this.loopCurrentLevel == 0) {
return;
}
if (this.suspendExitRunnable != null) {
this.queue.remove(this.suspendExitRunnable);
this.controllerRunnables.remove(this.suspendExitRunnable);
}
this.suspendExitRunnable= runnable;
if (Thread.currentThread() == this.controllerThread) {
runnable.run(this, null);
}
else {
scheduleControllerRunnable(runnable);
}
}
}
protected void runSuspendedLoopL(final int o) {
Queue.Section queueSection= null;
SystemRunnable updater= null;
final RunnableData savedCurrentRunnable= this.currentRunnable;
final RunnableProgressData savedProgressMonitor= this.runnableProgressData;
final ToolStatus savedStatus= this.status;
final int savedLower= this.suspendedLowerLevel;
final int savedLevel= this.suspendedLowerLevel= this.suspendedRunLevel;
try {
while (true) {
final int thisLevel;
synchronized (this.queue) {
thisLevel= this.suspendedRequestLevel;
if (thisLevel <= savedLevel) {
setSuspended(this.suspendedRequestLevel, 0, null);
return;
}
if (this.loopCurrentLevel != thisLevel || queueSection == null) {
this.loopCurrentLevel= this.suspendedRunLevel= thisLevel;
queueSection= this.queue.internal_addInsert(
new SuspendedInsertRunnable(thisLevel) );
if (savedLevel == 0 && updater == null) {
updater= new SuspendedUpdateRunnable();
this.queue.addOnIdle(updater, 6000);
}
}
this.suspendExitDetail= DebugEvent.UNSPECIFIED;
}
// run suspended loop
doRunSuspendedLoopL(o, thisLevel, queueSection);
// resume main runnable
final SuspendResumeRunnable runnable;
synchronized (this.queue) {
this.suspendExitDetail= DebugEvent.UNSPECIFIED;
if (this.isTerminated) {
setSuspended(0, 0, null);
return;
}
this.loopCurrentLevel= savedLevel;
this.suspendEnterDetail= DebugEvent.UNSPECIFIED;
this.queue.internal_removeInsert(queueSection);
queueSection= null;
if (thisLevel <= this.suspendedRequestLevel) {
continue;
}
runnable= this.suspendExitRunnable;
if (runnable != null) {
this.suspendExitRunnable= null;
this.queue.remove(runnable);
}
}
if (runnable != null) { // resume with runnable
try {
setCurrentRunnable((savedCurrentRunnable != null) ?
savedCurrentRunnable :
createRunnableData(runnable, this.queue.getCurrentSection()) );
if (runnable.canExec(savedProgressMonitor.getRoot())) { // exec resume
synchronized (this.queue) {
this.suspendExitDetail= runnable.detail;
loopChangeStatus(ToolStatus.STARTED_PROCESSING, savedProgressMonitor);
}
runnable.doExec(savedProgressMonitor.getRoot());
}
else { // cancel resume
synchronized (this.queue) {
this.suspendedRequestLevel= thisLevel;
}
}
}
catch (final Exception e) {
NicoCorePlugin.logError("An error occurred when executing debug command.",
e );
}
}
else { // resume without runnable
this.suspendExitDetail= DebugEvent.UNSPECIFIED;
if (savedCurrentRunnable != null) {
synchronized (this.queue) {
loopChangeStatus(ToolStatus.STARTED_PROCESSING, savedProgressMonitor);
}
}
}
}
}
finally {
this.suspendedLowerLevel= savedLower;
this.suspendedRunLevel= savedLevel;
setCurrentRunnable(savedCurrentRunnable);
synchronized (this.queue) {
loopChangeStatus(savedStatus, savedProgressMonitor);
// if not exit normally
if (this.loopCurrentLevel != savedLevel) {
this.loopCurrentLevel= savedLevel;
this.suspendEnterDetail= DebugEvent.UNSPECIFIED;
}
if (updater != null) {
this.queue.removeOnIdle(updater);
}
if (queueSection != null) {
this.queue.internal_removeInsert(queueSection);
}
this.suspendExitRunnable= null;
setSuspended(this.suspendedRequestLevel, 0, null);
}
}
}
protected void doRunSuspendedLoopL(final int o, final int level, final Section queueSection) {
loopSuspended(level, queueSection);
}
protected int getCurrentLevelL() {
return this.loopCurrentLevel;
}
protected int getRequestedLevelL() {
return this.suspendedRequestLevel;
}
public int getSuspendEnterDetail() {
return this.suspendEnterDetail;
}
public Object getSuspendEnterData() {
return this.suspendEnterData;
}
public int getSuspendExitDetail() {
return this.suspendExitDetail;
}
protected final void markAsTerminated() {
if (isToolAlive()) {
NicoCorePlugin.logError(NicoCore.STATUSCODE_RUNTIME_ERROR, "Illegal state: tool marked as terminated but still alive.", null); //$NON-NLS-1$
}
this.isTerminated= true;
}
protected final boolean isTerminated() {
return this.isTerminated;
}
/**
* Implement here special functionality to start the tool.
*
* The method is called automatically in the tool lifecycle thread.
*
* @param m a progress monitor
*
* @throws StatusException with details, if start fails.
*/
protected abstract void startToolL(ProgressMonitor m) throws StatusException;
/**
* Implement here special commands to kill the tool.
*
* The method is can be called async.
*
* @param a progress monitor
*/
protected abstract void killTool(ProgressMonitor m);
/**
* Checks if the tool is still alive.
*
* @return <code>true</code> if ok, otherwise <code>false</code>.
*/
protected abstract boolean isToolAlive();
/**
* Interrupts the tool. This methods can be called async.
*/
protected void interruptTool() throws UnsupportedOperationException {
final Thread thread= getControllerThread();
if (thread != null) {
thread.interrupt();
}
}
/**
* Is called, after termination is detected.
*
* @return exit code
*/
protected int finishToolL() {
return 0;
}
/**
* Implement here special commands to deallocate resources.
*
* Call super!
* The method is called automatically in the tool lifecycle thread
* after the tool is terminated.
*/
protected void clear() {
this.streams.dispose();
this.streams= null;
final ImIdentityList<Disposable> disposables= this.disposables.clearToList();
for (final Disposable disposable : disposables) {
try {
disposable.dispose();
}
catch (final Exception e) {
NicoCorePlugin.logError("An unexepected exception is thrown when disposing a controller extension.",
e );
}
}
}
@Override
public void handleStatus(final Status status, final ProgressMonitor m) {
if (status == null || status.getSeverity() == Status.OK) {
return;
}
final ToolCommandHandler handler= getCommandHandler(REPORT_STATUS_EVENT_ID);
if (handler != null) {
final Map<String, Object> data= Collections.singletonMap(REPORT_STATUS_DATA_KEY, (Object) status);
final Status reportStatus= executeHandler(REPORT_STATUS_EVENT_ID, handler, data, m);
if (reportStatus != null && reportStatus.getSeverity() == Status.OK) {
return;
}
}
if (status.getSeverity() > Status.INFO) {
logRunnableStatus(status, this.currentRunnable);
}
}
private void logRunnableStatus(final Status status, final RunnableData runnableData) {
NicoCorePlugin.log(new org.eclipse.core.runtime.MultiStatus(NicoCore.BUNDLE_ID, 0,
new IStatus[] { StatusUtils.convert(status) },
NicoCoreMessages.format_ProblemWhileRunningTask_message(
runnableData.runnable.getLabel(),
this.process ),
null ));
}
protected Status executeHandler(final String commandID, final ToolCommandHandler handler,
final Map<String, Object> data, final ProgressMonitor m) {
try {
return handler.execute(commandID, this, data, m);
}
catch (final Exception e) {
NicoCorePlugin.logError(NLS.bind("An error occurred when executing tool command ''{0}''.",
commandID ), e );
return null;
}
}
//-- RunnableAdapter - access only in main loop
protected void initRunnableAdapterL() {
this.fCurrentPrompt= this.fDefaultPrompt= this.workspaceData.getDefaultPrompt();
this.fLineSeparator= this.workspaceData.getLineSeparator();
}
@Override
public final ToolController getController() {
return this;
}
private RunnableData createRunnableData(final ToolRunnable runnable, final Section queueSection) {
return new RunnableData(runnable, getSubmitTypeL(runnable), queueSection);
}
private void setCurrentRunnable(final RunnableData runnable) {
this.currentRunnable= runnable;
this.currentSubmitType= runnable.submitType;
}
protected SubmitType getSubmitTypeL(final ToolRunnable runnable) {
if (runnable instanceof ConsoleRunnable) {
return ((ConsoleRunnable) runnable).getSubmitType();
}
else if (runnable instanceof SystemRunnable) {
return SubmitType.OTHER;
}
else {
return SubmitType.TOOLS;
}
}
@Override
public ToolRunnable getCurrentRunnable() {
return this.currentRunnable.runnable;
}
public Queue.Section getCurrentQueueSection() {
return this.currentRunnable.queueSection;
}
public SubmitType getCurrentSubmitType() {
return this.currentSubmitType;
}
public String getProperty(final String key) {
return null;
}
@Override
public final void refreshWorkspaceData(final int options,
final ProgressMonitor m) throws StatusException {
this.workspaceData.controlRefresh(options, this, m);
}
@Override
public ToolWorkspace getWorkspaceData() {
return this.workspaceData;
}
@Override
public boolean isDefaultPrompt() {
return (this.fDefaultPrompt == this.fCurrentPrompt);
}
@Override
public Prompt getPrompt() {
return this.fCurrentPrompt;
}
protected void setCurrentPromptL(final Prompt prompt) {
this.fCurrentPrompt= prompt;
this.workspaceData.controlSetCurrentPrompt(prompt, this.status);
}
protected void setDefaultPromptTextL(final String text) {
this.fDefaultPrompt= new Prompt(text, ConsoleService.META_PROMPT_DEFAULT);
this.workspaceData.controlSetDefaultPrompt(this.fDefaultPrompt);
}
protected void setLineSeparatorL(final String newSeparator) {
this.fLineSeparator= newSeparator;
this.workspaceData.controlSetLineSeparator(newSeparator);
}
protected void setFileSeparatorL(final char newSeparator) {
this.workspaceData.controlSetFileSeparator(newSeparator);
}
protected void setWorkspaceDirL(final IFileStore directory) {
this.workspaceData.controlSetWorkspaceDir(directory);
}
protected void setRemoteWorkspaceDirL(final IPath directory) {
this.workspaceData.controlSetRemoteWorkspaceDir(directory);
}
public final int getChangeStamp() {
return this.currentStamp;
}
private void touchChangeStamp() {
this.currentStamp= this.changeStamp= ((this.changeStamp + 1) & ~(1 << 31));
}
private void enableHotStamp() {
this.currentStamp= this.hotStamp= ((this.hotStamp + 1) | (1 << 31));
}
private void disableHotStamp() {
this.currentStamp= this.changeStamp;
}
public void briefAboutToChange() {
touchChangeStamp();
}
public void briefChanged(final int flags) {
briefChanged(null, flags);
}
public void briefChanged(final Object obj, final int flags) {
touchChangeStamp();
this.workspaceData.controlBriefChanged(obj, flags);
if (DEBUG_LOG_STATE) {
logChanged();
}
}
private void logEvents(final String label, final ToolStatus status) {
final ToStringBuilder sb= new ObjectUtils.ToStringBuilder(label);
sb.addProp("status", this.status); //$NON-NLS-1$
sb.addProp("changeStamp", this.changeStamp); //$NON-NLS-1$
sb.addProp("events", this.queue.internal_getEventList()); //$NON-NLS-1$
NicoCorePlugin.log(new InfoStatus(NicoCore.BUNDLE_ID, sb.toString()));
}
private void logChanged() {
final ToStringBuilder sb= new ObjectUtils.ToStringBuilder("changed"); //$NON-NLS-1$
sb.addProp("changeStamp", this.changeStamp); //$NON-NLS-1$
NicoCorePlugin.log(new InfoStatus(NicoCore.BUNDLE_ID, sb.toString()));
}
@Override
public void submitToConsole(final String input,
final ProgressMonitor m) throws StatusException {
this.fCurrentInput= input;
doBeforeSubmitL();
doSubmitL(m);
}
protected void doBeforeSubmitL() {
final ToolStreamProxy streams= getStreams();
final SubmitType submitType= getCurrentSubmitType();
streams.getInfoStreamMonitor().append(this.fCurrentPrompt.text, submitType,
this.fCurrentPrompt.meta);
streams.getInputStreamMonitor().append(this.fCurrentInput, submitType,
(this.fCurrentPrompt.meta & ConsoleService.META_HISTORY_DONTADD) );
streams.getInputStreamMonitor().append(this.workspaceData.getLineSeparator(), submitType,
ConsoleService.META_HISTORY_DONTADD);
}
protected abstract void doSubmitL(final ProgressMonitor m) throws StatusException;
protected StatusException cancelTask() {
return new StatusException(new CancelStatus(NicoCore.BUNDLE_ID,
Messages.ToolRunnable_error_RuntimeError_message,
null ));
}
protected abstract void doQuitL(final ProgressMonitor m) throws StatusException;
}