/*=============================================================================#
# Copyright (c) 2008-2016 Stephan Wahlbrink (WalWare.de) and others.
# 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.r.nico.impl;
import static de.walware.statet.nico.core.runtime.IToolEventHandler.LOGIN_ADDRESS_DATA_KEY;
import static de.walware.statet.nico.core.runtime.IToolEventHandler.LOGIN_CALLBACKS_DATA_KEY;
import static de.walware.statet.nico.core.runtime.IToolEventHandler.LOGIN_MESSAGE_DATA_KEY;
import static de.walware.statet.nico.core.runtime.IToolEventHandler.LOGIN_OK_EVENT_ID;
import static de.walware.statet.nico.core.runtime.IToolEventHandler.LOGIN_REQUEST_EVENT_ID;
import static de.walware.statet.nico.core.runtime.IToolEventHandler.LOGIN_USERNAME_DATA_KEY;
import java.io.InputStream;
import java.io.OutputStream;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.Registry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.locks.Lock;
import javax.security.auth.callback.Callback;
import javax.security.auth.login.LoginException;
import com.ibm.icu.text.DateFormat;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.osgi.util.NLS;
import de.walware.jcommons.lang.SystemUtils;
import de.walware.ecommons.ICommonStatusConstants;
import de.walware.ecommons.io.FileUtil;
import de.walware.ecommons.ltk.ast.IAstNode;
import de.walware.ecommons.ltk.core.model.IModelElement;
import de.walware.ecommons.ltk.core.model.ISourceUnit;
import de.walware.ecommons.net.RMIAddress;
import de.walware.ecommons.ts.IToolCommandHandler;
import de.walware.ecommons.ts.IToolRunnable;
import de.walware.ecommons.ts.IToolService;
import de.walware.statet.nico.core.runtime.IConsoleRunnable;
import de.walware.statet.nico.core.runtime.IRemoteEngineController;
import de.walware.statet.nico.core.runtime.SubmitType;
import de.walware.statet.nico.core.runtime.ToolProcess;
import de.walware.statet.nico.core.runtime.ToolStreamProxy;
import de.walware.statet.nico.core.util.TrackingConfiguration;
import de.walware.rj.RjException;
import de.walware.rj.data.RDataJConverter;
import de.walware.rj.data.REnvironment;
import de.walware.rj.data.RList;
import de.walware.rj.data.RObject;
import de.walware.rj.data.RObjectFactory;
import de.walware.rj.data.RReference;
import de.walware.rj.data.defaultImpl.RObjectFactoryImpl;
import de.walware.rj.eclient.graphics.comclient.ERClientGraphicActions;
import de.walware.rj.server.ConsoleEngine;
import de.walware.rj.server.ConsoleWriteCmdItem;
import de.walware.rj.server.DbgCmdItem;
import de.walware.rj.server.FxCallback;
import de.walware.rj.server.RjsComConfig;
import de.walware.rj.server.RjsStatus;
import de.walware.rj.server.Server;
import de.walware.rj.server.ServerInfo;
import de.walware.rj.server.ServerLogin;
import de.walware.rj.server.client.AbstractRJComClient;
import de.walware.rj.server.client.FunctionCallImpl;
import de.walware.rj.server.client.RClientGraphicFactory;
import de.walware.rj.server.client.RGraphicCreatorImpl;
import de.walware.rj.server.dbg.CallStack;
import de.walware.rj.server.dbg.CtrlReport;
import de.walware.rj.server.dbg.DbgEnablement;
import de.walware.rj.server.dbg.DbgFilterState;
import de.walware.rj.server.dbg.DbgRequest;
import de.walware.rj.server.dbg.ElementTracepointInstallationRequest;
import de.walware.rj.server.dbg.FlagTracepointInstallationRequest;
import de.walware.rj.server.dbg.FrameContext;
import de.walware.rj.server.dbg.FrameContextDetailRequest;
import de.walware.rj.server.dbg.SetDebugReport;
import de.walware.rj.server.dbg.SetDebugRequest;
import de.walware.rj.server.dbg.SrcfileData;
import de.walware.rj.server.dbg.TracepointEvent;
import de.walware.rj.server.dbg.TracepointInstallationReport;
import de.walware.rj.server.dbg.TracepointInstallationRequest;
import de.walware.rj.server.dbg.TracepointStatesUpdate;
import de.walware.rj.services.FunctionCall;
import de.walware.rj.services.RGraphicCreator;
import de.walware.rj.services.RPlatform;
import de.walware.rj.services.RServiceControlExtension;
import de.walware.statet.r.console.core.IRBasicAdapter;
import de.walware.statet.r.console.core.IRDataAdapter;
import de.walware.statet.r.console.core.RConsoleTool;
import de.walware.statet.r.console.core.RDbg;
import de.walware.statet.r.console.core.RProcess;
import de.walware.statet.r.console.core.RWorkspace;
import de.walware.statet.r.core.data.ICombinedRElement;
import de.walware.statet.r.core.model.IRElement;
import de.walware.statet.r.core.model.IRLangSourceElement;
import de.walware.statet.r.core.model.IRModelInfo;
import de.walware.statet.r.core.model.IRModelManager;
import de.walware.statet.r.core.model.IRWorkspaceSourceUnit;
import de.walware.statet.r.core.model.RElementName;
import de.walware.statet.r.core.model.RModel;
import de.walware.statet.r.core.rsource.ast.FDef;
import de.walware.statet.r.core.rsource.ast.RAst;
import de.walware.statet.r.core.rsource.ast.RAstNode;
import de.walware.statet.r.core.tool.IRConsoleService;
import de.walware.statet.r.internal.console.core.RConsoleCorePlugin;
import de.walware.statet.r.internal.nico.RNicoMessages;
import de.walware.statet.r.internal.rdata.CombinedElement;
import de.walware.statet.r.internal.rdata.CombinedFactory;
import de.walware.statet.r.nico.AbstractRDbgController;
import de.walware.statet.r.nico.ICombinedRDataAdapter;
import de.walware.statet.r.nico.IRModelSrcref;
import de.walware.statet.r.nico.IRSrcref;
import de.walware.statet.r.nico.RWorkspaceConfig;
/**
* Controller for RJ-Server
*/
public class RjsController extends AbstractRDbgController
implements IRemoteEngineController, IRDataAdapter, ICombinedRDataAdapter, RServiceControlExtension {
static {
RjsComConfig.registerRObjectFactory(CombinedFactory.FACTORY_ID, CombinedFactory.INSTANCE);
}
public static class RjsConnection {
private final RMIAddress fRMIAddress;
private final Server fServer;
private RjsConnection(final RMIAddress rmiAddress, final Server server) {
fRMIAddress = rmiAddress;
fServer = server;
}
public RMIAddress getRMIAddress() {
return fRMIAddress;
}
public Server getServer() {
return fServer;
}
}
public static RjsConnection lookup(final Registry registry, final RemoteException registryException,
final RMIAddress address) throws CoreException {
if (address == null) {
throw new NullPointerException();
}
final int[] clientVersion = AbstractRJComClient.version();
clientVersion[2] = -1;
final Server server;
int[] version;
try {
if (registryException != null) {
throw registryException;
}
server = (Server) registry.lookup(address.getName());
version = server.getVersion();
}
catch (final NotBoundException e) {
throw new CoreException(new Status(IStatus.ERROR, RConsoleCorePlugin.PLUGIN_ID,
ICommonStatusConstants.LAUNCHING,
"The specified R engine is not in the service registry (RMI).", e ));
}
catch (final RemoteException e) {
throw new CoreException(new Status(IStatus.ERROR, RConsoleCorePlugin.PLUGIN_ID,
ICommonStatusConstants.LAUNCHING,
NLS.bind("Cannot access the host/service registry (RMI) at ''{0}''.", address.getRegistryAddress()),
e ));
}
catch (final ClassCastException e) {
throw new CoreException(new Status(IStatus.ERROR, RConsoleCorePlugin.PLUGIN_ID,
ICommonStatusConstants.LAUNCHING,
NLS.bind("The specified R engine ({0}) is incompatibel to this client ({1}).", RjsUtil.getVersionString(null), RjsUtil.getVersionString(clientVersion)),
e ));
}
if (version.length != 3 || version[0] != clientVersion[0] || version[1] != clientVersion[1]) {
throw new CoreException(new Status(IStatus.ERROR, RConsoleCorePlugin.PLUGIN_ID,
ICommonStatusConstants.LAUNCHING,
NLS.bind("The specified R engine ({0}) is incompatibel to this client ({1}).", RjsUtil.getVersionString(version), RjsUtil.getVersionString(clientVersion)),
null ));
}
return new RjsConnection(address, server);
}
private static final IModelElement.Filter TAG_ELEMENT_FILTER = new IModelElement.Filter() {
@Override
public boolean include(final IModelElement element) {
return ((element.getElementType() & IRElement.MASK_C1) == IRElement.C1_METHOD);
}
};
private class NicoComClient extends AbstractRJComClient {
public NicoComClient() {
}
@Override
protected void initGraphicFactory() {
final IToolCommandHandler handler = getCommandHandler(INIT_RGRAPHIC_FACTORY_HANDLER_ID);
final Map<String, Object> data= new HashMap<>();
final IStatus status = executeHandler(INIT_RGRAPHIC_FACTORY_HANDLER_ID, handler, data, null);
final RClientGraphicFactory factory = (RClientGraphicFactory) data.get("factory"); //$NON-NLS-1$
if (status != null && status.isOK() && factory != null) {
setGraphicFactory(factory, new ERClientGraphicActions(this, getTool()));
}
}
@Override
protected void updateBusy(final boolean isBusy) {
// try {
fIsBusy = isBusy;
// }
// catch (Exception e) {
// }
}
@Override
protected void updatePrompt(final String text, final boolean addToHistory) {
try {
RjsController.this.setCurrentPromptL(text, addToHistory);
}
catch (final Exception e) {
}
}
@Override
protected void writeConsoleOutput(final byte streamId, final String text) {
try {
final ToolStreamProxy streams = getStreams();
final SubmitType submitType = getCurrentSubmitType();
switch (streamId) {
case ConsoleWriteCmdItem.R_OUTPUT:
streams.getOutputStreamMonitor().append(text, submitType, 0);
return;
case ConsoleWriteCmdItem.R_ERROR:
streams.getErrorStreamMonitor().append(text, submitType, 0);
return;
default:
streams.getSystemOutputMonitor().append(text, submitType, 0);
return;
}
}
catch (final Exception e) {
}
}
@Override
protected void showMessage(final String text) {
try {
final ToolStreamProxy streams = getStreams();
final SubmitType submitType = getCurrentSubmitType();
streams.getInfoStreamMonitor().append(text, submitType, 0);
}
catch (final Exception e) {
}
}
@Override
protected RList handleUICallback(String commandId, final RList args,
final IProgressMonitor monitor) throws Exception {
// TODO: allow handlers to use RJ data objects
// TODO: allow handlers to return values
// TODO: provide extension point for event handlers
IToolCommandHandler handler = getCommandHandler(commandId);
if (handler == null && commandId.startsWith("r/")) {
final String s = commandId.substring(2);
handler = getCommandHandler(s);
if (handler != null) {
commandId = s;
}
}
if (handler != null) {
final RDataJConverter converter = new RDataJConverter();
converter.setKeepArray1(false);
converter.setRObjectFactory(fRObjectFactory);
final Map<String, Object> javaArgs= new HashMap<>();
if (args != null) {
for (int i = 0; i < args.getLength(); i++) {
javaArgs.put(args.getName(i), converter.toJava(args.get(i)));
}
}
final IStatus status = handler.execute(commandId, RjsController.this, javaArgs, monitor);
switch (status.getSeverity()) {
case IStatus.OK:
break;
default:
throw new CoreException(status);
}
Map<String, Object> javaAnswer = null;
if (commandId.equals("common/chooseFile")) { //$NON-NLS-1$
javaAnswer = Collections.singletonMap(
"filename", javaArgs.get("filename") ); //$NON-NLS-1$ //$NON-NLS-2$
}
if (javaAnswer != null) {
final RList answer = (RList) converter.toRJ(javaAnswer);
return answer;
}
else {
return null;
}
}
return super.handleUICallback(commandId, args, monitor);
}
@Override
protected void handleDbgEvent(final byte dbgOp, final Object event) {
if (dbgOp == DbgCmdItem.OP_NOTIFY_TP_EVENT) {
handle((TracepointEvent) event);
}
super.handleDbgEvent(dbgOp, event);
}
@Override
protected void log(final IStatus status) {
RConsoleCorePlugin.log(status);
}
@Override
protected void handleServerStatus(final RjsStatus serverStatus, final IProgressMonitor monitor) throws CoreException {
String specialMessage = null;
switch (serverStatus.getCode()) {
case 0:
return;
case Server.S_DISCONNECTED:
fConnectionState = Server.S_DISCONNECTED;
//$FALL-THROUGH$
case Server.S_LOST:
if (fConnectionState == Server.S_DISCONNECTED) {
specialMessage = RNicoMessages.R_Info_Disconnected_message;
break;
}
else if (!fEmbedded) {
fConnectionState = Server.S_LOST;
specialMessage = RNicoMessages.R_Info_ConnectionLost_message;
break;
}
//$FALL-THROUGH$
case Server.S_STOPPED:
fConnectionState = Server.S_STOPPED;
specialMessage = RNicoMessages.R_Info_Stopped_message;
break;
default:
throw new IllegalStateException();
}
if (!isClosed()) {
markAsTerminated();
setClosed(true);
handleStatus(new Status(IStatus.INFO, RConsoleCorePlugin.PLUGIN_ID, addTimestampToMessage(specialMessage, System.currentTimeMillis())), monitor);
}
throw new CoreException(new Status(IStatus.CANCEL, RConsoleCorePlugin.PLUGIN_ID, specialMessage));
}
@Override
protected void handleStatus(final Status status, final IProgressMonitor monitor) {
RjsController.this.handleStatus(status, monitor);
}
@Override
protected void processHotMode() {
runHotModeLoop();
}
@Override
protected void processExtraMode(final int position) {
runSuspendedLoopL(SUSPENDED_DEEPLEVEL);
}
@Override
protected void scheduleConnectionCheck() {
synchronized (getQueue()) {
if (getStatusL().isWaiting()) {
scheduleControllerRunnable(new ControllerSystemRunnable(
"r/check", "Connection Check") { //$NON-NLS-1$
@Override
public void run(final IToolService s,
final IProgressMonitor monitor) throws CoreException {
fRjs.runMainLoopPing(monitor);
}
});
}
}
}
}
private final RMIAddress fAddress;
private final String[] fRArgs;
private boolean fIsBusy = true;
private final RjsConnection fRjsConnection;
private final NicoComClient fRjs = new NicoComClient();
private int fRjsId;
private final boolean fEmbedded;
private final boolean fStartup;
private final Map<String, Object> fRjsProperties;
private int fConnectionState;
private final RObjectFactory fRObjectFactory = RObjectFactoryImpl.INSTANCE;
/**
*
* @param process the R process the controller belongs to
* @param address the RMI address
* @param initData the initialization data
* @param embedded flag if running in embedded mode
* @param startup flag to start R (otherwise connect only)
* @param rArgs R arguments (required only if startup is <code>true</code>)
* @param initialWD
*/
public RjsController(final RProcess process,
final RMIAddress address, final RjsConnection connection, final Map<String, Object> initData,
final boolean embedded, final boolean startup, final String[] rArgs,
final Map<String, Object> rjsProperties, final IFileStore initialWD,
final RWorkspaceConfig workspaceConfig,
final List<TrackingConfiguration> trackingConfigurations) {
super(process, initData);
if (address == null || connection == null) {
throw new IllegalArgumentException();
}
process.registerFeatureSet(RConsoleTool.R_DATA_FEATURESET_ID);
process.registerFeatureSet("de.walware.rj.services.RService"); //$NON-NLS-1$
if (!embedded) {
process.registerFeatureSet(IRemoteEngineController.FEATURE_SET_ID);
}
fAddress = address;
fRjsConnection = connection;
fEmbedded = embedded;
fStartup = startup;
fRArgs = rArgs;
fRjsProperties = (rjsProperties != null) ? rjsProperties : new HashMap<String, Object>();
setTracksConfig(trackingConfigurations);
setWorksapceData(new RWorkspace(this,
(embedded || address.isLocalHost()) ? null :
address.getHostAddress().getHostAddress(), workspaceConfig ));
setWorkspaceDirL(initialWD);
initRunnableAdapterL();
}
@Override
public boolean supportsBusy() {
return true;
}
@Override
public boolean isBusy() {
return fIsBusy;
}
@Override
public boolean isDisconnected() {
return (fConnectionState == Server.S_DISCONNECTED || fConnectionState == Server.S_LOST);
}
/**
* This is an async operation
* cancel is not supported by this implementation
*
* @param monitor a progress monitor
*/
@Override
public void disconnect(final IProgressMonitor monitor) throws CoreException {
switch (getStatus()) {
case STARTED_IDLING:
case STARTED_SUSPENDED:
case STARTED_PROCESSING:
case STARTED_PAUSED:
monitor.beginTask("Disconnecting from R remote engine...", 1);
synchronized (getQueue()) {
beginInternalTask();
}
try {
fRjs.getConsoleServer().disconnect();
fConnectionState = Server.S_DISCONNECTED;
}
catch (final RemoteException e) {
throw new CoreException(new Status(IStatus.ERROR, RConsoleCorePlugin.PLUGIN_ID,
ICommonStatusConstants.LAUNCHING,
"Disconnecting from R remote engine failed.", e));
}
finally {
synchronized (getQueue()) {
scheduleControllerRunnable(new ControllerSystemRunnable(
"common/disconnect/finish", "Disconnect") { //$NON-NLS-1$
@Override
public void run(final IToolService s,
final IProgressMonitor monitor) throws CoreException {
if (!isTerminated()) {
fRjs.runMainLoopPing(monitor);
fRjs.handleServerStatus(new RjsStatus(RjsStatus.INFO, Server.S_DISCONNECTED), monitor);
}
}
});
endInternalTask();
}
monitor.done();
}
}
}
@Override
protected IToolRunnable createStartRunnable() {
return new StartRunnable() {
@Override
public String getLabel() {
return "Connect to and load remote R engine.";
}
};
}
@Override
protected void startToolL(final IProgressMonitor monitor) throws CoreException {
fRjsId = RjsComConfig.registerClientComHandler(fRjs);
fRjs.initClient(getTool(), this, fRjsProperties, fRjsId);
try {
final Map<String, Object> data= new HashMap<>();
final IToolCommandHandler loginHandler = getCommandHandler(LOGIN_REQUEST_EVENT_ID);
String msg = null;
boolean connected = false;
while (!connected) {
final Map<String, Object> initData = getInitData();
final ServerLogin login = fRjsConnection.getServer().createLogin(Server.C_CONSOLE_CONNECT);
try {
final Callback[] callbacks = login.getCallbacks();
if (callbacks != null) {
final List<Callback> checked= new ArrayList<>();
FxCallback fx = null;
for (final Callback callback : callbacks) {
if (callback instanceof FxCallback) {
fx = (FxCallback) callback;
}
else {
checked.add(callback);
}
}
if (initData != null) {
data.putAll(initData);
}
data.put(LOGIN_ADDRESS_DATA_KEY, (fx != null) ? fAddress.getHost() : fAddress.getAddress());
data.put(LOGIN_MESSAGE_DATA_KEY, msg);
data.put(LOGIN_CALLBACKS_DATA_KEY, checked.toArray(new Callback[checked.size()]));
if (loginHandler == null) {
throw new CoreException(new Status(IStatus.ERROR, RConsoleCorePlugin.PLUGIN_ID,
ICommonStatusConstants.LAUNCHING,
"Login requested but not supported by this configuration.", null ));
}
if (!loginHandler.execute(LOGIN_REQUEST_EVENT_ID, this, data, monitor).isOK()) {
throw new CoreException(Status.CANCEL_STATUS);
}
if (fx != null) {
RjsUtil.handleFxCallback(RjsUtil.getSession(data, new SubProgressMonitor(monitor, 1)), fx, new SubProgressMonitor(monitor, 1));
}
}
msg = null;
if (monitor.isCanceled()) {
throw new CoreException(Status.CANCEL_STATUS);
}
final Map<String, Object> args= new HashMap<>();
args.putAll(fRjsProperties);
ConsoleEngine rjServer;
if (fStartup) {
args.put("args", fRArgs); //$NON-NLS-1$
rjServer = (ConsoleEngine) fRjsConnection.getServer().execute(Server.C_CONSOLE_START, args, login.createAnswer());
}
else {
rjServer = (ConsoleEngine) fRjsConnection.getServer().execute(Server.C_CONSOLE_CONNECT, args, login.createAnswer());
}
fRjs.setServer(rjServer, 0);
connected = true;
if (callbacks != null) {
loginHandler.execute(LOGIN_OK_EVENT_ID, this, data, monitor);
if (initData != null) {
initData.put(LOGIN_USERNAME_DATA_KEY, data.get(LOGIN_USERNAME_DATA_KEY));
}
}
}
catch (final LoginException e) {
msg = e.getLocalizedMessage();
}
finally {
if (login != null) {
login.clearData();
}
}
}
final ServerInfo info = fRjsConnection.getServer().getInfo();
if (getWorkspaceData().isRemote()) {
try {
final String wd = FileUtil.toString(getWorkspaceData().toFileStore(info.getDirectory()));
if (wd != null) {
setStartupWD(wd);
}
}
catch (final CoreException e) {}
try {
String sep= fRjs.getProperty(SystemUtils.FILE_SEPARATOR_KEY);
if (sep == null) {
final String osName= fRjs.getProperty(SystemUtils.OS_NAME_KEY);
if (osName != null && SystemUtils.isOSWindows(osName)) {
sep= "\\"; //$NON-NLS-1$
}
}
if (sep != null && !sep.isEmpty()) {
setFileSeparatorL(sep.charAt(0));
}
}
catch (final Exception e) {}
}
else {
setStartupWD(info.getDirectory());
}
final long timestamp = info.getTimestamp();
if (timestamp != 0) {
setStartupTimestamp(timestamp);
}
final List<IStatus> warnings= new ArrayList<>();
initTracks(info.getDirectory(), monitor, warnings);
if (fStartup && !this.startupsRunnables.isEmpty()) {
getQueue().add(this.startupsRunnables);
this.startupsRunnables.clear();
}
if (!fStartup) {
handleStatus(new Status(IStatus.INFO, RConsoleCorePlugin.PLUGIN_ID,
addTimestampToMessage(RNicoMessages.R_Info_Reconnected_message, getTool().getConnectionTimestamp()) ),
monitor);
}
// fRjs.runMainLoop(null, null, monitor); must not wait at server side
fRjs.activateConsole();
class RStart2Runnable extends ControllerSystemRunnable implements IConsoleRunnable {
RStart2Runnable() {
super("r/rj/start2", "Finish Initialization / Read Output"); //$NON-NLS-1$
}
@Override
public SubmitType getSubmitType() {
return SubmitType.CONSOLE;
}
@Override
public void run(final IToolService s,
final IProgressMonitor monitor) throws CoreException {
if (!fRjs.isConsoleReady()) { // R is still working
fRjs.runMainLoop(null, null, monitor);
briefChanged(IRConsoleService.AUTO_CHANGE);
}
for (final IStatus status : warnings) {
handleStatus(status, monitor);
}
}
}
scheduleControllerRunnable(new RStart2Runnable());
}
catch (final RemoteException e) {
throw new CoreException(new Status(IStatus.ERROR, RConsoleCorePlugin.PLUGIN_ID,
ICommonStatusConstants.LAUNCHING,
"The R engine could not be started.", e ));
}
catch (final RjException e) {
throw new CoreException(new Status(IStatus.ERROR, RConsoleCorePlugin.PLUGIN_ID,
ICommonStatusConstants.LAUNCHING,
"An error occured when creating login data.", e ));
}
}
// public void controlNotification(final RjsComObject com) throws RemoteException {
// if (com instanceof RjsStatus) {
// final RjsStatusImpl2 serverStatus = (RjsStatusImpl2) com;
// if (serverStatus.getCode() == Server.S_DISCONNECTED || serverStatus.getCode() == Server.S_STOPPED) {
// scheduleControllerRunnable(new IToolRunnable() {
// public String getTypeId() {
// return null;
// }
// public String getLabel() {
// return "Update State";
// }
// public SubmitType getSubmitType() {
// return SubmitType.OTHER;
// }
// public void changed(final int event, final ToolProcess process) {
// }
// public void run(final IToolRunnableControllerAdapter tools, final IProgressMonitor monitor)
// throws InterruptedException, CoreException {
// if (!isTerminated()) {
// rjsHandleStatus(serverStatus, monitor);
// }
// }
//
// });
// }
// }
// }
protected String addTimestampToMessage(final String message, final long timestamp) {
final String datetime = DateFormat.getDateTimeInstance().format(System.currentTimeMillis());
return datetime + " - " + message; //$NON-NLS-1$
}
@Override
protected void requestHotMode(final boolean async) {
fRjs.requestHotMode(async);
}
@Override
protected boolean initilizeHotMode() {
return fRjs.startHotMode();
}
@Override
protected void onHotModeExit(final IProgressMonitor monitor) {
try {
this.fRjs.finishTask(monitor);
}
catch (final Throwable e) {}
}
@Override
protected void onTaskFinished(final IToolRunnable runnable, final int event,
final IProgressMonitor monitor) {
try {
this.fRjs.finishTask(monitor);
}
catch (final Throwable e) {}
super.onTaskFinished(runnable, event, monitor);
}
@Override
protected int setSuspended(final int level, final int enterDetail, final Object enterData) {
final int diff = super.setSuspended(level, enterDetail, enterData);
if (level > 0 && diff > 0) {
fRjs.requestExtraMode(
(AbstractRJComClient.EXTRA_BEFORE | AbstractRJComClient.EXTRA_NESTED) );
}
return diff;
}
@Override
protected CallStack doEvalCallStack(final IProgressMonitor monitor) throws CoreException {
return (CallStack) fRjs.execSyncDbgOp(DbgCmdItem.OP_LOAD_FRAME_LIST,
null, monitor );
}
@Override
protected FrameContext doEvalFrameContext(final int position,
final IProgressMonitor monitor) throws Exception {
return (FrameContext) fRjs.execSyncDbgOp(DbgCmdItem.OP_LOAD_FRAME_CONTEXT,
new FrameContextDetailRequest(position), monitor );
}
@Override
protected void interruptTool() throws UnsupportedOperationException {
fRjs.runAsyncInterrupt();
}
@Override
protected void postCancelTask(final int options, final IProgressMonitor monitor) throws CoreException {
super.postCancelTask(options, monitor);
fCurrentInput = ""; //$NON-NLS-1$
doSubmitL(monitor);
fCurrentInput = ""; //$NON-NLS-1$
doSubmitL(monitor);
}
@Override
protected boolean isToolAlive() {
if (fConnectionState != 0 || !fRjs.runAsyncPing()) {
return false;
}
if (Thread.currentThread() == getControllerThread() && !isInHotModeL()
&& !fRjs.isConsoleReady()) {
return false;
}
return true;
}
@Override
protected void killTool(final IProgressMonitor monitor) {
fRjs.setClosed(true);
final ToolProcess consoleProcess = getTool();
// TODO: kill remote command?
final IProcess[] processes = consoleProcess.getLaunch().getProcesses();
for (int i = 0; i < processes.length; i++) {
if (processes[i] != consoleProcess && !processes[i].isTerminated()) {
try {
processes[i].terminate();
}
catch (final Exception e) {
}
}
}
}
@Override
protected void clear() {
fRjs.setClosed(true);
super.clear();
if (fEmbedded && !isDisconnected()) {
try {
Naming.unbind(fAddress.getAddress());
}
catch (final Throwable e) {
}
}
fRjs.disposeAllGraphics();
if (fRjsId > 0) {
RjsComConfig.unregisterClientComHandler(fRjsId);
fRjsId = 0;
}
}
@Override
protected int finishToolL() {
int exitCode = 0;
if (isDisconnected()) {
exitCode = ToolProcess.EXITCODE_DISCONNECTED;
}
return exitCode;
}
@Override
protected boolean canSuspend(final IProgressMonitor monitor) {
return (fRjs.getDataLevel() == 0);
}
@Override
protected void doRequestSuspend(final IProgressMonitor monitor) throws CoreException {
fRjs.execSyncDbgOp(DbgCmdItem.OP_REQUEST_SUSPEND,
null, monitor );
}
@Override
protected SetDebugReport doExec(final SetDebugRequest request,
final IProgressMonitor monitor) throws CoreException {
return (SetDebugReport) fRjs.execSyncDbgOp(DbgCmdItem.OP_SET_DEBUG, request, monitor);
}
@Override
protected CtrlReport doExec(final DbgRequest request,
final IProgressMonitor monitor) throws CoreException {
return (CtrlReport) fRjs.execSyncDbgOp(request.getOp(), request, monitor);
}
@Override
protected void doPrepareSrcfile(final String srcfile, final String statetPath,
final IProgressMonitor monitor) throws CoreException {
final FunctionCall prepare = createFunctionCall("rj:::.statet.prepareSrcfile");
prepare.addChar("filename", srcfile);
prepare.addChar("path", statetPath);
prepare.evalVoid(monitor);
}
@Override
public TracepointInstallationReport exec(final TracepointInstallationRequest request,
final IProgressMonitor monitor) throws CoreException {
if (request instanceof FlagTracepointInstallationRequest) {
return (TracepointInstallationReport) fRjs.execSyncDbgOp(
DbgCmdItem.OP_INSTALL_TP_FLAGS, request, monitor );
}
else if (request instanceof ElementTracepointInstallationRequest) {
return (TracepointInstallationReport) fRjs.execSyncDbgOp(
DbgCmdItem.OP_INSTALL_TP_POSITIONS, request, monitor );
}
else {
throw new IllegalArgumentException("request type not supported");
}
}
@Override
public void exec(final DbgEnablement request) throws CoreException {
fRjs.execAsyncDbgOp(DbgCmdItem.OP_SET_ENABLEMENT, request);
}
@Override
public void exec(final DbgFilterState request) throws CoreException {
fRjs.execAsyncDbgOp(DbgCmdItem.OP_RESET_FILTER_STATE, request);
}
@Override
public void exec(final TracepointStatesUpdate request) throws CoreException {
fRjs.execAsyncDbgOp(DbgCmdItem.OP_UPDATE_TP_STATES, request);
}
@Override
public void exec(final TracepointStatesUpdate request,
final IProgressMonitor monitor) throws CoreException {
fRjs.execSyncDbgOp(DbgCmdItem.OP_UPDATE_TP_STATES, request, monitor);
}
@Override
protected void doSubmitCommandL(final String[] lines, final SrcfileData srcfile,
final IRSrcref srcref,
final IProgressMonitor monitor) throws CoreException {
if ((fCurrentPrompt.meta & (IRBasicAdapter.META_PROMPT_DEFAULT | IRBasicAdapter.META_PROMPT_SUSPENDED)) == 0) {
super.doSubmitCommandL(lines, srcfile, srcref, monitor);
return;
}
final FunctionCall prepare = createFunctionCall("rj:::.statet.prepareCommand");
prepare.add("lines", fRObjectFactory.createVector(fRObjectFactory.createCharData(lines)));
if (srcfile != null && srcref != null) {
final List<String> attributeNames= new ArrayList<>();
final List<RObject> attributeValues= new ArrayList<>();
if (srcfile.getName() != null) {
prepare.addChar("filename", srcfile.getName());
}
// if (srcfile.workspacePath != null) {
// attributeNames.add("statet.Path");
// attributeValues.add(fRObjectFactory.createVector(fRObjectFactory.createCharData(
// new String[] { srcfile.workspacePath } )));
// }
if (srcfile.getTimestamp() != 0) {
attributeNames.add("timestamp");
attributeValues.add(fRObjectFactory.createVector(fRObjectFactory.createNumData(
new double[] { srcfile.getTimestamp() } )));
}
final int[] rjSrcref = RDbg.createRJSrcref(srcref);
if (rjSrcref != null) {
attributeNames.add("linesSrcref");
attributeValues.add(fRObjectFactory.createVector(fRObjectFactory.createIntData(
rjSrcref )));
}
if (attributeNames.size() > 0) {
prepare.add("srcfileAttributes", fRObjectFactory.createList(
attributeValues.toArray(new RObject[attributeValues.size()]),
attributeNames.toArray(new String[attributeNames.size()]) ));
}
if (srcref instanceof IRModelSrcref) {
// Move to abstract controller or breakpoint adapter?
final IRModelSrcref modelSrcref = (IRModelSrcref) srcref;
final List<IRLangSourceElement> elements = modelSrcref.getElements();
if (elements.size() > 0) {
final List<String> elementIds= new ArrayList<>(elements.size());
final List<RObject> elementIndexes= new ArrayList<>(elements.size());
for (final IRLangSourceElement element : elements) {
if (TAG_ELEMENT_FILTER.include(element)) {
final FDef fdef = element.getAdapter(FDef.class);
if (fdef != null) {
final String elementId = RDbg.getElementId(element);
final RAstNode cont = fdef.getContChild();
final int[] path = RAst.computeRExpressionIndex(cont,
RAst.getRRootNode(cont, modelSrcref) );
if (elementId != null && path != null) {
final int[] fullPath = new int[path.length+1];
fullPath[0] = 1;
System.arraycopy(path, 0, fullPath, 1, path.length);
elementIds.add(elementId);
elementIndexes.add(fRObjectFactory.createVector(
fRObjectFactory.createIntData(fullPath)));
}
}
}
}
if (elementIds.size() > 0) {
prepare.add("elementIds", fRObjectFactory.createList(
elementIndexes.toArray(new RObject[elementIndexes.size()]),
elementIds.toArray(new String[elementIds.size()]) ));
}
}
}
}
prepare.evalVoid(monitor);
final boolean addToHistory = (fCurrentPrompt.meta & IRBasicAdapter.META_HISTORY_DONTADD) == 0;
fCurrentInput = lines[0];
doBeforeSubmitL();
for (int i = 1; i < lines.length; i++) {
setCurrentPromptL(this.continuePromptText, addToHistory);
fCurrentInput = lines[i];
doBeforeSubmitL();
}
fCurrentInput = "rj:::.statet.evalCommand()";
doSubmitL(monitor);
}
@Override
public void doSubmitFileCommandToConsole(final String[] lines,
final SrcfileData srcfile, final ISourceUnit su,
final IProgressMonitor monitor) throws CoreException {
if (srcfile != null && su instanceof IRWorkspaceSourceUnit
&& su.getModelTypeId() == RModel.TYPE_ID) {
try {
final IRModelInfo modelInfo = (IRModelInfo) su.getModelInfo(RModel.TYPE_ID,
IRModelManager.MODEL_FILE, monitor );
if (modelInfo != null) {
final IRLangSourceElement fileElement = modelInfo.getSourceElement();
final RAstNode rootNode = (RAstNode) fileElement.getAdapter(IAstNode.class);
final List<? extends IRLangSourceElement> elements = modelInfo.getSourceElement()
.getSourceChildren(TAG_ELEMENT_FILTER);
final List<String> elementIds= new ArrayList<>(elements.size());
final List<RObject> elementIndexes= new ArrayList<>(elements.size());
for (final IRLangSourceElement element : elements) {
final FDef fdef = element.getAdapter(FDef.class);
if (fdef != null) {
final String elementId = RDbg.getElementId(element);
final RAstNode cont = fdef.getContChild();
final int[] path = RAst.computeRExpressionIndex(cont, rootNode);
if (elementId != null && path != null) {
elementIds.add(elementId);
elementIndexes.add(fRObjectFactory.createVector(
fRObjectFactory.createIntData(path)));
}
}
}
final FunctionCall prepare = createFunctionCall("rj:::.statet.prepareSource"); //$NON-NLS-1$
prepare.add(fRObjectFactory.createList(new RObject[] {
fRObjectFactory.createVector(fRObjectFactory.createCharData(
new String[] { srcfile.getPath() })),
fRObjectFactory.createVector(fRObjectFactory.createNumData(
new double[] { srcfile.getTimestamp() })),
fRObjectFactory.createVector(fRObjectFactory.createIntData(
new int[] { rootNode.getChildCount() })),
fRObjectFactory.createList(
elementIndexes.toArray(new RObject[elementIndexes.size()]),
elementIds.toArray(new String[elementIds.size()]) ),
}, new String[] { "path", "timestamp", "exprsLength", "elementIds" })); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
prepare.evalVoid(monitor);
}
}
catch (final CoreException e) {
RConsoleCorePlugin.log(new Status(IStatus.ERROR, RConsoleCorePlugin.PLUGIN_ID, -1,
NLS.bind("An error occurred when preparing element tagging for file ''{0}''.",
srcfile.getPath() ), e ));
}
}
super.doSubmitFileCommandToConsole(lines, srcfile, su, monitor);
}
@Override
protected void doSubmitL(final IProgressMonitor monitor) throws CoreException {
fRjs.answerConsole(fCurrentInput + fLineSeparator, monitor);
}
@Override
public String getProperty(final String key) {
return fRjs.getProperty(key);
}
@Override
public RPlatform getPlatform() {
return fRjs.getRPlatform();
}
@Override
public void evalVoid(final String command, final IProgressMonitor monitor) throws CoreException {
fRjs.evalVoid(command, null, monitor);
}
@Override
public void evalVoid(final String command, final RObject envir,
final IProgressMonitor monitor) throws CoreException {
fRjs.evalVoid(command, envir, monitor);
}
@Override
public RObject evalData(final String command, final IProgressMonitor monitor) throws CoreException {
return fRjs.evalData(command, null, null, 0, -1, monitor);
}
@Override
public RObject evalData(final String command, final String factoryId,
final int options, final int depth, final IProgressMonitor monitor) throws CoreException {
return fRjs.evalData(command, null, factoryId, options, depth, monitor);
}
@Override
public RObject evalData(final String command, final RObject envir,
final String factoryId, final int options, final int depth,
final IProgressMonitor monitor) throws CoreException {
return fRjs.evalData(command, envir, factoryId, options, depth, monitor);
}
@Override
public RObject evalData(final RReference reference, final IProgressMonitor monitor) throws CoreException {
return fRjs.evalData(reference, null, 0, -1, monitor);
}
@Override
public RObject evalData(final RReference reference, final String factoryId,
final int options, final int depth, final IProgressMonitor monitor) throws CoreException {
return fRjs.evalData(reference, factoryId, options, depth, monitor);
}
@Override
public RObject[] findData(final String symbol, final RObject envir, final boolean inherits,
final String factoryId, final int options, final int depth,
final IProgressMonitor monitor) throws CoreException {
return fRjs.findData(symbol, envir, inherits, factoryId, options, depth, monitor);
}
@Override
public ICombinedRElement evalCombinedStruct(final String command,
final int options, final int depth, final RElementName name, final IProgressMonitor monitor) throws CoreException {
final RObject data = this.fRjs.evalData(command, null,
CombinedFactory.FACTORY_ID, (options | RObjectFactory.F_ONLY_STRUCT), depth,
monitor );
if (data instanceof CombinedElement) {
final CombinedElement e = (CombinedElement) data;
if (name != null) {
e.setElementName(name);
}
return e;
}
return null;
}
@Override
public ICombinedRElement evalCombinedStruct(final String command, final RObject envir,
final int options, final int depth, final RElementName name,
final IProgressMonitor monitor) throws CoreException {
final RObject data= this.fRjs.evalData(command, envir,
CombinedFactory.FACTORY_ID, (options | RObjectFactory.F_ONLY_STRUCT), depth,
monitor );
if (data instanceof CombinedElement) {
final CombinedElement e = (CombinedElement) data;
if (name != null) {
e.setElementName(name);
}
return e;
}
return null;
}
private ICombinedRElement evalCombinedStructSpecialEnv(final RElementName name,
final int options, final int depth,
final IProgressMonitor monitor) throws CoreException {
final byte envType;
switch (name.getType()) {
case RElementName.SCOPE_NS:
envType= REnvironment.ENVTYPE_NAMESPACE_EXPORTS;
break;
case RElementName.SCOPE_NS_INT:
envType= REnvironment.ENVTYPE_NAMESPACE;
break;
default:
throw new IllegalArgumentException();
}
final RObject data= this.fRjs.evalData(envType, name.getSegmentName(),
CombinedFactory.FACTORY_ID, (options | RObjectFactory.F_ONLY_STRUCT), depth,
monitor );
if (data instanceof CombinedElement) {
final CombinedElement e = (CombinedElement) data;
e.setElementName(name);
return e;
}
return null;
}
@Override
public ICombinedRElement evalCombinedStruct(final RElementName name,
final int options, final int depth,
final IProgressMonitor monitor) throws CoreException {
switch (name.getType()) {
case RElementName.SCOPE_NS:
case RElementName.SCOPE_NS_INT:
if (name.getNextSegment() == null) {
return evalCombinedStructSpecialEnv(name, options, depth, monitor);
}
break;
default:
break;
}
final String command = RElementName.createDisplayName(name, RElementName.DISPLAY_FQN | RElementName.DISPLAY_EXACT);
if (command == null) {
throw new CoreException(new Status(IStatus.ERROR, RConsoleCorePlugin.PLUGIN_ID, 0, "Illegal R element name.", null));
}
return evalCombinedStruct(command, options, depth, name, monitor);
}
@Override
public ICombinedRElement evalCombinedStruct(final RReference reference,
final int options, final int depth, final RElementName name,
final IProgressMonitor monitor) throws CoreException {
final RObject data = evalData(reference, CombinedFactory.FACTORY_ID, (options | RObjectFactory.F_ONLY_STRUCT), depth, monitor);
if (data instanceof CombinedElement) {
final CombinedElement e = (CombinedElement) data;
if (name != null) {
e.setElementName(name);
}
return e;
}
return null;
}
@Override
public void assignData(final String expression, final RObject data,
final IProgressMonitor monitor) throws CoreException {
fRjs.assignData(expression, data, null, monitor);
}
@Override
public void downloadFile(final OutputStream out, final String fileName, final int options,
final IProgressMonitor monitor) throws CoreException {
fRjs.downloadFile(out, fileName, options, monitor);
}
@Override
public byte[] downloadFile(final String fileName, final int options,
final IProgressMonitor monitor) throws CoreException {
return fRjs.downloadFile(fileName, options, monitor);
}
@Override
public void uploadFile(final InputStream in, final long length, final String fileName, final int options,
final IProgressMonitor monitor) throws CoreException {
fRjs.uploadFile(in, length, fileName, options, monitor);
}
@Override
public FunctionCall createFunctionCall(final String name) throws CoreException {
return new FunctionCallImpl(fRjs, name, fRObjectFactory);
}
@Override
public RGraphicCreator createRGraphicCreator(final int options) throws CoreException {
return new RGraphicCreatorImpl(this, fRjs, options);
}
@Override
public void addCancelHandler(final Callable<Boolean> handler) {
fRjs.addCancelHandler(handler);
}
@Override
public void removeCancelHandler(final Callable<Boolean> handler) {
fRjs.removeCancelHandler(handler);
}
@Override
public Lock getWaitLock() {
return fRjs.getWaitLock();
}
@Override
public void waitingForUser(final IProgressMonitor monitor) {
fRjs.waitingForUser();
}
@Override
public void resume() {
fRjs.resume();
}
}