blob: a8d1b31dbd58b181359625f7832f9cca52b5117e [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2008, 2021 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.r.nico.impl;
import static org.eclipse.statet.nico.core.runtime.IToolEventHandler.LOGIN_ADDRESS_DATA_KEY;
import static org.eclipse.statet.nico.core.runtime.IToolEventHandler.LOGIN_CALLBACKS_DATA_KEY;
import static org.eclipse.statet.nico.core.runtime.IToolEventHandler.LOGIN_MESSAGE_DATA_KEY;
import static org.eclipse.statet.nico.core.runtime.IToolEventHandler.LOGIN_OK_EVENT_ID;
import static org.eclipse.statet.nico.core.runtime.IToolEventHandler.LOGIN_REQUEST_EVENT_ID;
import static org.eclipse.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.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.IStatus;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.osgi.util.NLS;
import org.eclipse.statet.jcommons.collections.ImList;
import org.eclipse.statet.jcommons.lang.NonNull;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.jcommons.lang.SystemUtils;
import org.eclipse.statet.jcommons.rmi.RMIAddress;
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.ProgressMonitor;
import org.eclipse.statet.jcommons.status.Status;
import org.eclipse.statet.jcommons.status.StatusException;
import org.eclipse.statet.jcommons.status.eplatform.EStatusUtils;
import org.eclipse.statet.jcommons.ts.core.BasicToolCommandData;
import org.eclipse.statet.jcommons.ts.core.ToolCommandData;
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.io.FileUtil;
import org.eclipse.statet.internal.r.console.core.RConsoleCorePlugin;
import org.eclipse.statet.internal.r.nico.RNicoMessages;
import org.eclipse.statet.internal.r.rdata.BasicCombinedRElement;
import org.eclipse.statet.internal.r.rdata.CombinedFactory;
import org.eclipse.statet.internal.r.rdata.REnvironmentVar;
import org.eclipse.statet.ltk.ast.core.AstNode;
import org.eclipse.statet.ltk.model.core.element.LtkModelElementFilter;
import org.eclipse.statet.ltk.model.core.element.SourceUnit;
import org.eclipse.statet.nico.core.runtime.ConsoleRunnable;
import org.eclipse.statet.nico.core.runtime.IRemoteEngineController;
import org.eclipse.statet.nico.core.runtime.SubmitType;
import org.eclipse.statet.nico.core.runtime.ToolProcess;
import org.eclipse.statet.nico.core.runtime.ToolStreamProxy;
import org.eclipse.statet.nico.core.util.TrackingConfiguration;
import org.eclipse.statet.r.console.core.IRBasicAdapter;
import org.eclipse.statet.r.console.core.IRDataAdapter;
import org.eclipse.statet.r.console.core.RConsoleTool;
import org.eclipse.statet.r.console.core.RDbg;
import org.eclipse.statet.r.console.core.RProcess;
import org.eclipse.statet.r.console.core.RWorkspace;
import org.eclipse.statet.r.core.data.CombinedRElement;
import org.eclipse.statet.r.core.model.RElement;
import org.eclipse.statet.r.core.model.RElementName;
import org.eclipse.statet.r.core.model.RLangSourceElement;
import org.eclipse.statet.r.core.model.RModel;
import org.eclipse.statet.r.core.model.RModelManager;
import org.eclipse.statet.r.core.model.RSourceUnitModelInfo;
import org.eclipse.statet.r.core.model.RWorkspaceSourceUnit;
import org.eclipse.statet.r.core.rsource.ast.FDef;
import org.eclipse.statet.r.core.rsource.ast.RAstNode;
import org.eclipse.statet.r.core.rsource.ast.RAsts;
import org.eclipse.statet.r.core.tool.IRConsoleService;
import org.eclipse.statet.r.nico.AbstractRDbgController;
import org.eclipse.statet.r.nico.ICombinedRDataAdapter;
import org.eclipse.statet.r.nico.IRModelSrcref;
import org.eclipse.statet.r.nico.IRSrcref;
import org.eclipse.statet.r.nico.RWorkspaceConfig;
import org.eclipse.statet.rj.RjException;
import org.eclipse.statet.rj.data.REnvironment;
import org.eclipse.statet.rj.data.RLanguage;
import org.eclipse.statet.rj.data.RList;
import org.eclipse.statet.rj.data.RObject;
import org.eclipse.statet.rj.data.RObjectFactory;
import org.eclipse.statet.rj.data.RReference;
import org.eclipse.statet.rj.data.impl.DefaultRObjectFactory;
import org.eclipse.statet.rj.data.impl.RLanguageImpl;
import org.eclipse.statet.rj.eclient.graphics.comclient.ERClientGraphicActions;
import org.eclipse.statet.rj.server.ConsoleWriteCmdItem;
import org.eclipse.statet.rj.server.DbgCmdItem;
import org.eclipse.statet.rj.server.FxCallback;
import org.eclipse.statet.rj.server.REngine;
import org.eclipse.statet.rj.server.RjsComConfig;
import org.eclipse.statet.rj.server.RjsStatus;
import org.eclipse.statet.rj.server.Server;
import org.eclipse.statet.rj.server.ServerInfo;
import org.eclipse.statet.rj.server.ServerLogin;
import org.eclipse.statet.rj.server.client.AbstractRJComClient;
import org.eclipse.statet.rj.server.client.FunctionCallImpl;
import org.eclipse.statet.rj.server.client.RClientGraphicFactory;
import org.eclipse.statet.rj.server.client.RGraphicCreatorImpl;
import org.eclipse.statet.rj.server.dbg.CallStack;
import org.eclipse.statet.rj.server.dbg.CtrlReport;
import org.eclipse.statet.rj.server.dbg.DbgEnablement;
import org.eclipse.statet.rj.server.dbg.DbgFilterState;
import org.eclipse.statet.rj.server.dbg.DbgRequest;
import org.eclipse.statet.rj.server.dbg.ElementTracepointInstallationRequest;
import org.eclipse.statet.rj.server.dbg.FlagTracepointInstallationRequest;
import org.eclipse.statet.rj.server.dbg.FrameContext;
import org.eclipse.statet.rj.server.dbg.FrameContextDetailRequest;
import org.eclipse.statet.rj.server.dbg.SetDebugReport;
import org.eclipse.statet.rj.server.dbg.SetDebugRequest;
import org.eclipse.statet.rj.server.dbg.SrcfileData;
import org.eclipse.statet.rj.server.dbg.TracepointEvent;
import org.eclipse.statet.rj.server.dbg.TracepointInstallationRequest;
import org.eclipse.statet.rj.server.dbg.TracepointStatesUpdate;
import org.eclipse.statet.rj.services.BasicFQRObject;
import org.eclipse.statet.rj.services.FQRObject;
import org.eclipse.statet.rj.services.FunctionCall;
import org.eclipse.statet.rj.services.RGraphicCreator;
import org.eclipse.statet.rj.services.RPlatform;
import org.eclipse.statet.rj.services.RServiceControlExtension;
import org.eclipse.statet.rj.ts.core.RTool;
import org.eclipse.statet.rj.ts.core.util.RjToolCommandData;
/**
* 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 rmiAddress;
private final Server server;
private RjsConnection(final RMIAddress rmiAddress, final Server server) {
this.rmiAddress= rmiAddress;
this.server= server;
}
public RMIAddress getRMIAddress() {
return this.rmiAddress;
}
public Server getServer() {
return this.server;
}
}
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 org.eclipse.core.runtime.Status(IStatus.ERROR, RConsoleCorePlugin.BUNDLE_ID,
"The specified R engine is not in the service registry (RMI).", e ));
}
catch (final RemoteException e) {
throw new CoreException(new org.eclipse.core.runtime.Status(IStatus.ERROR, RConsoleCorePlugin.BUNDLE_ID,
NLS.bind("Cannot access the host/service registry (RMI) at ''{0}''.", address.getRegistryAddress()),
e ));
}
catch (final ClassCastException e) {
throw new CoreException(new org.eclipse.core.runtime.Status(IStatus.ERROR, RConsoleCorePlugin.BUNDLE_ID,
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 org.eclipse.core.runtime.Status(IStatus.ERROR, RConsoleCorePlugin.BUNDLE_ID,
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);
}
public static final int RJS_LOCAL= 1 << 0;
public static final int RJS_SETUP_CONSOLE= 1 << 8;
private static final LtkModelElementFilter<RLangSourceElement> TAG_ELEMENT_FILTER= new LtkModelElementFilter<>() {
@Override
public boolean include(final RLangSourceElement element) {
return ((element.getElementType() & RElement.MASK_C1) == RElement.C1_METHOD);
}
};
private class NicoComClient extends AbstractRJComClient {
public NicoComClient() {
}
@Override
protected void initGraphicFactory() {
final ToolCommandHandler handler= getCommandHandler(INIT_RGRAPHIC_FACTORY_HANDLER_ID);
final ToolCommandData data= new BasicToolCommandData();
final Status status= executeHandler(INIT_RGRAPHIC_FACTORY_HANDLER_ID, handler, data, null);
final RClientGraphicFactory factory= data.get("factory", RClientGraphicFactory.class); //$NON-NLS-1$
if (status != null && status.getSeverity() < Status.ERROR && factory != null) {
setGraphicFactory(factory, new ERClientGraphicActions(this, getTool()));
}
}
@Override
protected void updateBusy(final boolean isBusy) {
// try {
RjsController.this.isBusy= 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 boolean acceptsAnswer,
final ProgressMonitor m) throws Exception {
// TODO: provide extension point for event handlers
ToolCommandHandler 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 var data= new RjToolCommandData(args) {
@NonNull RList getAnswer() {
return getRJReturnData(RjsController.this.fRObjectFactory);
}
};
final Status status= handler.execute(commandId, RjsController.this, data, m);
switch (status.getSeverity()) {
case Status.OK:
break;
default:
throw new StatusException(status);
}
if (acceptsAnswer && data.hasReturnData()) {
return data.getAnswer();
}
else {
return null;
}
}
return super.handleUICallback(commandId, args, acceptsAnswer, m);
}
@Override
protected void handleDbgEvents(final byte dbgOp, final Object events) {
if (dbgOp == DbgCmdItem.OP_NOTIFY_TP_EVENTS) {
handleTracepointEvents((List<TracepointEvent>) events);
}
super.handleDbgEvents(dbgOp, events);
}
@Override
protected void log(final Status status) {
RConsoleCorePlugin.log(status);
}
@Override
protected void handleServerStatus(final RjsStatus serverStatus,
final ProgressMonitor m) throws StatusException {
String specialMessage= null;
switch (serverStatus.getCode()) {
case 0:
return;
case Server.S_DISCONNECTED:
RjsController.this.fConnectionState= Server.S_DISCONNECTED;
//$FALL-THROUGH$
case Server.S_LOST:
if (RjsController.this.fConnectionState == Server.S_DISCONNECTED) {
specialMessage= RNicoMessages.R_Info_Disconnected_message;
break;
}
else if ((RjsController.this.rjsFlags & RJS_LOCAL) == 0) {
RjsController.this.fConnectionState= Server.S_LOST;
specialMessage= RNicoMessages.R_Info_ConnectionLost_message;
break;
}
//$FALL-THROUGH$
case Server.S_STOPPED:
RjsController.this.fConnectionState= Server.S_STOPPED;
specialMessage= RNicoMessages.R_Info_Stopped_message;
break;
default:
throw new IllegalStateException();
}
if (!isClosed()) {
markAsTerminated();
setClosed(true);
handleStatus(new InfoStatus(RConsoleCorePlugin.BUNDLE_ID,
addTimestampToMessage(specialMessage, System.currentTimeMillis()) ),
m );
}
throw new StatusException(new CancelStatus(RConsoleCorePlugin.BUNDLE_ID, specialMessage));
}
@Override
protected void handleStatus(final Status status, final ProgressMonitor m) {
RjsController.this.handleStatus(status, m);
}
@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 ToolService s,
final ProgressMonitor m) throws StatusException {
RjsController.this.fRjs.runMainLoopPing(m);
}
});
}
}
}
}
private final RMIAddress address;
private final String[] rArgs;
private boolean isBusy= true;
private final RjsConnection rjsConnection;
private final NicoComClient fRjs= new NicoComClient();
private int fRjsId;
private final int rjsFlags;
private final Map<String, Object> rjsProperties;
private int fConnectionState;
private final RObjectFactory fRObjectFactory= DefaultRObjectFactory.INSTANCE;
/**
*
* @param process the R process the controller belongs to
* @param address the RMI address
* @param connectionInfo the initialization data
* @param rjsFlags controller mode flags
* @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> connectionInfo,
final int rjsFlags, final String[] rArgs,
final Map<String, Object> rjsProperties, final IFileStore initialWD,
final RWorkspaceConfig workspaceConfig,
final List<TrackingConfiguration> trackingConfigurations) {
super(process, connectionInfo);
if (address == null || connection == null) {
throw new IllegalArgumentException();
}
this.address= address;
this.rjsConnection= connection;
this.rjsFlags= rjsFlags;
this.rArgs= rArgs;
this.rjsProperties= (rjsProperties != null) ? rjsProperties : new HashMap<>();
process.registerFeatureSet(RTool.R_SERVICE_FEATURE_ID);
process.registerFeatureSet(RConsoleTool.R_DATA_FEATURESET_ID);
if ((this.rjsFlags & RJS_LOCAL) == 0) {
process.registerFeatureSet(IRemoteEngineController.FEATURE_SET_ID);
}
setTracksConfig(trackingConfigurations);
setWorksapceData(new RWorkspace(this,
((this.rjsFlags & RJS_LOCAL) != 0 || address.isLocalHost()) ? null :
address.getHostAddress().getHostAddress(), workspaceConfig ));
setWorkspaceDirL(initialWD);
initRunnableAdapterL();
}
@Override
public boolean supportsBusy() {
return true;
}
@Override
public boolean isBusy() {
return this.isBusy;
}
@Override
public boolean isDisconnected() {
return (this.fConnectionState == Server.S_DISCONNECTED || this.fConnectionState == Server.S_LOST);
}
/**
* This is an async operation
* cancel is not supported by this implementation
*
* @param m a progress monitor
*/
@Override
public void disconnect(final ProgressMonitor m) throws StatusException {
switch (getStatus()) {
case STARTED_IDLING:
case STARTED_SUSPENDED:
case STARTED_PROCESSING:
case STARTED_PAUSED:
m.beginTask("Disconnecting from R remote engine...", 1);
synchronized (getQueue()) {
beginInternalTask();
}
try {
this.fRjs.getConsoleEngine().disconnect();
this.fConnectionState= Server.S_DISCONNECTED;
}
catch (final RemoteException e) {
throw new StatusException(new ErrorStatus(RConsoleCorePlugin.BUNDLE_ID,
"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 ToolService s,
final ProgressMonitor m) throws StatusException {
if (!isTerminated()) {
RjsController.this.fRjs.runMainLoopPing(m);
RjsController.this.fRjs.handleServerStatus(new RjsStatus(RjsStatus.INFO, Server.S_DISCONNECTED), m);
}
}
});
endInternalTask();
}
}
}
}
@Override
protected ToolRunnable createStartRunnable() {
return new StartRunnable() {
@Override
public String getLabel() {
return "Connect to and load remote R engine.";
}
};
}
@Override
protected void startToolL(final ProgressMonitor m) throws StatusException {
this.fRjsId= RjsComConfig.registerClientComHandler(this.fRjs);
this.fRjs.initClient(getTool(), this, this.rjsProperties, this.fRjsId);
try {
final ToolCommandHandler loginHandler= getCommandHandler(LOGIN_REQUEST_EVENT_ID);
String msg= null;
boolean connected= false;
int attempt= 0;
while (!connected) {
final Map<String, Object> connectionInfo= getTool().getConnectionInfo();
final ServerLogin login= this.rjsConnection.getServer().createLogin(Server.C_CONSOLE_CONNECT);
try {
final ToolCommandData data;
final ImList<Callback> callbacks= login.getCallbacks();
if (!callbacks.isEmpty()) {
if (loginHandler == null) {
throw new StatusException(new ErrorStatus(RConsoleCorePlugin.BUNDLE_ID,
"Login requested but not supported by this configuration." ));
}
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);
}
}
{ final var dataMap= new HashMap<String, Object>();
if (connectionInfo != null) {
dataMap.putAll(connectionInfo);
}
dataMap.put(LOGIN_ADDRESS_DATA_KEY, (fx != null) ? this.address.getHost() : this.address.getAddress());
dataMap.put(LOGIN_MESSAGE_DATA_KEY, msg);
dataMap.put(LOGIN_CALLBACKS_DATA_KEY, checked.toArray(new Callback[checked.size()]));
dataMap.put("attempt", ++attempt);
data= new BasicToolCommandData(dataMap);
}
if (loginHandler.execute(LOGIN_REQUEST_EVENT_ID, this, data, m).getSeverity() != Status.OK) {
throw new StatusException(Status.CANCEL_STATUS);
}
if (fx != null) {
RjsUtil.handleFxCallback(
RjsUtil.getSession(data, m.newSubMonitor(1)),
fx, m.newSubMonitor(1) );
}
}
else {
data= null;
}
msg= null;
m.checkCanceled();
final Map<String, Object> args= new HashMap<>();
args.putAll(this.rjsProperties);
REngine rjServer;
if ((this.rjsFlags & RJS_SETUP_CONSOLE) != 0) {
args.put("args", this.rArgs); //$NON-NLS-1$
rjServer= (REngine)this.rjsConnection.getServer().execute(
Server.C_CONSOLE_START, args, login.createAnswer() );
}
else {
rjServer= (REngine)this.rjsConnection.getServer().execute(
Server.C_CONSOLE_CONNECT, args, login.createAnswer() );
}
this.fRjs.setServer(rjServer, 0);
connected= true;
if (data != null) {
loginHandler.execute(LOGIN_OK_EVENT_ID, this, data, m);
if (connectionInfo != null) {
connectionInfo.put(LOGIN_USERNAME_DATA_KEY, data.getString(LOGIN_USERNAME_DATA_KEY));
}
}
}
catch (final LoginException e) {
msg= e.getLocalizedMessage();
}
finally {
if (login != null) {
login.clearData();
}
}
}
final ServerInfo info= this.rjsConnection.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= this.fRjs.getProperty(SystemUtils.FILE_SEPARATOR_KEY);
if (sep == null) {
final String osName= this.fRjs.getProperty(SystemUtils.OS_NAME_KEY);
if (osName != null && SystemUtils.getOs(osName) == SystemUtils.OS_WIN) {
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<Status> warnings= new ArrayList<>();
initTracks(m.newSubMonitor(1), warnings);
if ((this.rjsFlags & RJS_SETUP_CONSOLE) != 0 && !this.startupsRunnables.isEmpty()) {
getQueue().add(this.startupsRunnables);
this.startupsRunnables.clear();
}
if ((this.rjsFlags & RJS_SETUP_CONSOLE) == 0) {
handleStatus(new InfoStatus(RConsoleCorePlugin.BUNDLE_ID,
addTimestampToMessage(RNicoMessages.R_Info_Reconnected_message, getTool().getConnectionTimestamp()) ),
m);
}
// fRjs.runMainLoop(null, null, monitor); must not wait at server side
this.fRjs.activateConsole();
class RStart2Runnable extends ControllerSystemRunnable implements ConsoleRunnable {
RStart2Runnable() {
super("r/rj/start2", "Finish Initialization / Read Output"); //$NON-NLS-1$
}
@Override
public SubmitType getSubmitType() {
return SubmitType.CONSOLE;
}
@Override
public void run(final ToolService s,
final ProgressMonitor m) throws StatusException {
if (!RjsController.this.fRjs.isConsoleReady()) { // R is still working
RjsController.this.fRjs.runMainLoop(null, null, m);
briefChanged(IRConsoleService.AUTO_CHANGE);
}
for (final Status status : warnings) {
handleStatus(status, m);
}
}
}
scheduleControllerRunnable(new RStart2Runnable());
}
catch (final RemoteException e) {
throw new StatusException(new ErrorStatus(RConsoleCorePlugin.BUNDLE_ID,
"The R engine could not be started.", e ));
}
catch (final RjException e) {
throw new StatusException(new ErrorStatus(RConsoleCorePlugin.BUNDLE_ID,
"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 ProgressMonitor m)
// 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) {
this.fRjs.requestHotMode(async);
}
@Override
protected boolean initilizeHotMode() {
return this.fRjs.startHotMode();
}
@Override
protected void onHotModeExit(final ProgressMonitor m) {
super.onHotModeExit(m);
try {
this.fRjs.finishTask(m);
}
catch (final Throwable e) {}
}
@Override
protected void onTaskFinished(final RunnableData runnableData, final int event,
final ProgressMonitor m) {
try {
this.fRjs.finishTask(m);
}
catch (final Throwable e) {}
super.onTaskFinished(runnableData, event, m);
}
@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) {
this.fRjs.requestExtraMode(
(AbstractRJComClient.EXTRA_BEFORE | AbstractRJComClient.EXTRA_NESTED) );
}
return diff;
}
@Override
protected CallStack doEvalCallStack(final ProgressMonitor m) throws StatusException {
return (CallStack) this.fRjs.execSyncDbgOp(DbgCmdItem.OP_LOAD_FRAME_LIST,
null, m );
}
@Override
protected FrameContext doEvalFrameContext(final int position,
final ProgressMonitor m) throws Exception {
return (FrameContext) this.fRjs.execSyncDbgOp(DbgCmdItem.OP_LOAD_FRAME_CONTEXT,
new FrameContextDetailRequest(position), m );
}
@Override
protected void interruptTool() throws UnsupportedOperationException {
this.fRjs.runAsyncInterrupt();
}
@Override
protected void postCancelTask(final int options,
final ProgressMonitor m) throws StatusException {
super.postCancelTask(options, m);
if (this.fRjs.isConsoleReady()) {
this.fCurrentInput= ""; //$NON-NLS-1$
doSubmitL(m);
this.fCurrentInput= ""; //$NON-NLS-1$
doSubmitL(m);
}
else {
// reschedule?
}
}
@Override
protected boolean isToolAlive() {
if (this.fConnectionState != 0 || !this.fRjs.runAsyncPing()) {
return false;
}
if (Thread.currentThread() == getControllerThread() && !isInHotModeL()
&& !this.fRjs.isConsoleReady()) {
return false;
}
return true;
}
@Override
protected void killTool(final ProgressMonitor m) {
this.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() {
this.fRjs.setClosed(true);
super.clear();
if ((this.rjsFlags & RJS_LOCAL) != 0 && !isDisconnected()) {
try {
Naming.unbind(this.address.getAddress());
}
catch (final Throwable e) {
}
}
this.fRjs.disposeAllGraphics();
if (this.fRjsId > 0) {
RjsComConfig.unregisterClientComHandler(this.fRjsId);
this.fRjsId= 0;
}
}
@Override
protected int finishToolL() {
int exitCode= 0;
if (isDisconnected()) {
exitCode= ToolProcess.EXITCODE_DISCONNECTED;
}
return exitCode;
}
@Override
protected boolean canSuspend(final ProgressMonitor m) {
return (this.fRjs.getDataLevel() == 0);
}
@Override
protected void doRequestSuspend(final ProgressMonitor m) throws StatusException {
this.fRjs.execSyncDbgOp(DbgCmdItem.OP_REQUEST_SUSPEND,
null, m );
}
@Override
protected SetDebugReport doExec(final SetDebugRequest request,
final ProgressMonitor m) throws StatusException {
return (SetDebugReport) this.fRjs.execSyncDbgOp(DbgCmdItem.OP_SET_DEBUG, request, m);
}
@Override
protected CtrlReport doExec(final DbgRequest request,
final ProgressMonitor m) throws StatusException {
return (CtrlReport) this.fRjs.execSyncDbgOp(request.getOp(), request, m);
}
@Override
protected void doPrepareSrcfile(final String srcfile, final String statetPath,
final ProgressMonitor m) throws StatusException {
final FunctionCall prepare= createFunctionCall("rj:::.statet.prepareSrcfile");
prepare.addChar("filename", srcfile);
prepare.addChar("path", statetPath);
prepare.evalVoid(m);
}
@Override
public void exec(final TracepointInstallationRequest request,
final ProgressMonitor m) throws StatusException {
if (request instanceof FlagTracepointInstallationRequest) {
this.fRjs.execSyncDbgOp(DbgCmdItem.OP_INSTALL_TP_FLAGS, request, m);
}
else if (request instanceof ElementTracepointInstallationRequest) {
this.fRjs.execSyncDbgOp(DbgCmdItem.OP_INSTALL_TP_POSITIONS, request, m);
}
else {
throw new IllegalArgumentException("request type not supported");
}
}
@Override
public void exec(final DbgEnablement request) throws StatusException {
this.fRjs.execAsyncDbgOp(DbgCmdItem.OP_SET_ENABLEMENT, request);
}
@Override
public void exec(final DbgFilterState request) throws StatusException {
this.fRjs.execAsyncDbgOp(DbgCmdItem.OP_RESET_FILTER_STATE, request);
}
@Override
public void exec(final TracepointStatesUpdate request) throws StatusException {
this.fRjs.execAsyncDbgOp(DbgCmdItem.OP_UPDATE_TP_STATES, request);
}
@Override
public void exec(final TracepointStatesUpdate request,
final ProgressMonitor m) throws StatusException {
this.fRjs.execSyncDbgOp(DbgCmdItem.OP_UPDATE_TP_STATES, request, m);
}
@Override
protected void doSubmitCommandL(final String[] lines, final SrcfileData srcfile,
final IRSrcref srcref,
final ProgressMonitor m) throws StatusException {
if ((this.fCurrentPrompt.meta & (IRBasicAdapter.META_PROMPT_DEFAULT | IRBasicAdapter.META_PROMPT_SUSPENDED)) == 0) {
super.doSubmitCommandL(lines, srcfile, srcref, m);
return;
}
final FunctionCall prepare= createFunctionCall("rj:::.statet.prepareCommand");
prepare.add("lines", this.fRObjectFactory.createVector(this.fRObjectFactory.createCharData(lines)));
if (srcfile != null && srcref != null && srcref.getFirstLine() >= 0) {
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(this.fRObjectFactory.createVector(this.fRObjectFactory.createNumData(
new double[] { srcfile.getTimestamp() } )));
}
attributeNames.add("linesSrcref");
attributeValues.add(this.fRObjectFactory.createVector(this.fRObjectFactory.createIntData(
RDbg.createRJSrcref(srcref) )));
if (attributeNames.size() > 0) {
prepare.add("srcfileAttributes", this.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<RLangSourceElement> 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 RLangSourceElement element : elements) {
if (RjsController.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= RAsts.computeRExpressionIndex(cont,
RAsts.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(this.fRObjectFactory.createVector(
this.fRObjectFactory.createIntData(fullPath)));
}
}
}
}
if (elementIds.size() > 0) {
prepare.add("elementIds", this.fRObjectFactory.createList(
elementIndexes.toArray(new RObject[elementIndexes.size()]),
elementIds.toArray(new String[elementIds.size()]) ));
}
}
}
}
prepare.evalVoid(m);
final boolean addToHistory= (this.fCurrentPrompt.meta & IRBasicAdapter.META_HISTORY_DONTADD) == 0;
this.fCurrentInput= lines[0];
doBeforeSubmitL();
for (int i= 1; i < lines.length; i++) {
setCurrentPromptL(this.continuePromptText, addToHistory);
this.fCurrentInput= lines[i];
doBeforeSubmitL();
}
this.fCurrentInput= "rj:::.statet.evalCommand()";
doSubmitL(m);
}
@Override
public void doSubmitFileCommandToConsole(final String[] lines,
final SrcfileData srcfile, final SourceUnit su,
final ProgressMonitor m) throws StatusException {
if (srcfile != null && su instanceof RWorkspaceSourceUnit
&& su.getModelTypeId() == RModel.R_TYPE_ID) {
try {
final RSourceUnitModelInfo modelInfo= (RSourceUnitModelInfo) su.getModelInfo(RModel.R_TYPE_ID,
RModelManager.MODEL_FILE, EStatusUtils.convert(m.newSubMonitor(0)) );
if (modelInfo != null) {
final RLangSourceElement fileElement= modelInfo.getSourceElement();
final RAstNode rootNode= (RAstNode) fileElement.getAdapter(AstNode.class);
final List<? extends RLangSourceElement> elements= modelInfo.getSourceElement()
.getSourceChildren(RjsController.TAG_ELEMENT_FILTER);
final List<String> elementIds= new ArrayList<>(elements.size());
final List<RObject> elementIndexes= new ArrayList<>(elements.size());
for (final RLangSourceElement 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= RAsts.computeRExpressionIndex(cont, rootNode);
if (elementId != null && path != null) {
elementIds.add(elementId);
elementIndexes.add(this.fRObjectFactory.createVector(
this.fRObjectFactory.createIntData(path)));
}
}
}
final FunctionCall prepare= createFunctionCall("rj:::.statet.prepareSource"); //$NON-NLS-1$
prepare.add(this.fRObjectFactory.createList(new RObject[] {
this.fRObjectFactory.createVector(this.fRObjectFactory.createCharData(
new String[] { srcfile.getPath() })),
this.fRObjectFactory.createVector(this.fRObjectFactory.createNumData(
new double[] { srcfile.getTimestamp() })),
this.fRObjectFactory.createVector(this.fRObjectFactory.createIntData(
new int[] { rootNode.getChildCount() })),
this.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(m);
}
}
catch (final StatusException e) {
RConsoleCorePlugin.logError(NLS.bind(
"An error occurred when preparing element tagging for file ''{0}''.",
srcfile.getPath() ),
e );
}
}
super.doSubmitFileCommandToConsole(lines, srcfile, su, m);
}
@Override
protected void doSubmitL(final ProgressMonitor m) throws StatusException {
this.fRjs.answerConsole(this.fCurrentInput + this.fLineSeparator, m);
}
@Override
public String getProperty(final String key) {
return this.fRjs.getProperty(key);
}
@Override
public RPlatform getPlatform() {
return this.fRjs.getRPlatform();
}
@Override
public void evalVoid(final String command,
final ProgressMonitor m) throws StatusException {
this.fRjs.evalVoid(command, null,
m );
}
@Override
public void evalVoid(final String command, final @Nullable RObject envir,
final ProgressMonitor m) throws StatusException {
this.fRjs.evalVoid(command, envir,
m );
}
@Override
public RObject evalData(final String command,
final ProgressMonitor m) throws StatusException {
return this.fRjs.evalData(command, null,
null, 0, -1, m );
}
@Override
public RObject evalData(final String command, final @Nullable String factoryId,
final int options, final int depth,
final ProgressMonitor m) throws StatusException {
return this.fRjs.evalData(command, null,
factoryId, options, depth, m );
}
@Override
public RObject evalData(final String command, final @Nullable RObject envir,
final @Nullable String factoryId, final int options, final int depth,
final ProgressMonitor m) throws StatusException {
return this.fRjs.evalData(command, envir,
factoryId, options, depth, m );
}
@Override
public RObject evalData(final RReference reference,
final ProgressMonitor m) throws StatusException {
return this.fRjs.evalData(reference, null, 0, -1,
m );
}
@Override
public RObject evalData(final RReference reference,
final @Nullable String factoryId, final int options, final int depth,
final ProgressMonitor m) throws StatusException {
return this.fRjs.evalData(reference,
factoryId, options, depth, m );
}
@Override
public FQRObject<RProcess> findData(final String symbol, final @Nullable RObject env, final boolean inherits,
final @Nullable String factoryId, final int options, final int depth,
final ProgressMonitor m) throws StatusException {
final RObject[] data= this.fRjs.findData(symbol, env, inherits,
factoryId, options, depth, m );
if (data != null) {
return new BasicFQRObject<>(getTool(), (REnvironment) data[1], symbol, data[0]);
}
return null;
}
private BasicCombinedRElement checkCombinedRElement(final @Nullable RObject data,
final int options, final @Nullable RElementName name) {
if (data instanceof BasicCombinedRElement) {
final BasicCombinedRElement e= (BasicCombinedRElement) data;
if (e.getRObjectType() == RObject.TYPE_ENVIRONMENT) {
((REnvironmentVar) e).setSource(getTool(), getChangeStamp(), options);
}
if (name != null) {
e.setElementName(name);
}
return e;
}
return null;
}
@Override
public CombinedRElement evalCombinedStruct(final String command,
final int options, final int depth, final @Nullable RElementName name,
final ProgressMonitor m) throws StatusException {
final RObject data= this.fRjs.evalData(command, null,
CombinedFactory.FACTORY_ID, (options | RObjectFactory.F_ONLY_STRUCT), depth,
m );
return checkCombinedRElement(data, options, name);
}
@Override
public CombinedRElement evalCombinedStruct(final String command, final @Nullable RObject envir,
final int options, final int depth, final @Nullable RElementName name,
final ProgressMonitor m) throws StatusException {
final RObject data= this.fRjs.evalData(command, envir,
CombinedFactory.FACTORY_ID, (options | RObjectFactory.F_ONLY_STRUCT), depth,
m );
return checkCombinedRElement(data, options, name);
}
private CombinedRElement evalCombinedStructSpecialEnv(final RElementName name,
final int options, final int depth,
final ProgressMonitor m) throws StatusException {
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,
m );
return checkCombinedRElement(data, options, name);
}
@Override
public CombinedRElement evalCombinedStruct(final RElementName name,
final int options, final int depth,
final ProgressMonitor m) throws StatusException {
switch (name.getType()) {
case RElementName.SCOPE_NS:
case RElementName.SCOPE_NS_INT:
if (name.getNextSegment() == null) {
return evalCombinedStructSpecialEnv(name, options, depth, m);
}
break;
default:
break;
}
final String command= name.getDisplayName(RElementName.DISPLAY_FQN | RElementName.DISPLAY_EXACT);
if (command == null) {
throw new StatusException(new ErrorStatus(RConsoleCorePlugin.BUNDLE_ID, "Illegal R element name."));
}
return evalCombinedStruct(command, options, depth, name, m);
}
@Override
public CombinedRElement evalCombinedStruct(final RReference reference,
final int options, final int depth, final @Nullable RElementName name,
final ProgressMonitor m) throws StatusException {
final RObject data= evalData(reference,
CombinedFactory.FACTORY_ID, (options | RObjectFactory.F_ONLY_STRUCT), depth,
m );
return checkCombinedRElement(data, options, name);
}
private boolean isValidSymbol(final RElementName name) {
return (name.getType() == RElementName.MAIN_DEFAULT && name.getSegmentName() != null
&& name.getNextSegment() == null );
}
private RLanguage createLangObject(final RElementName name, final String arg) {
final String expr= name.getDisplayName(RElementName.DISPLAY_FQN | RElementName.DISPLAY_EXACT);
if (expr == null) {
throw new IllegalArgumentException(arg);
}
return new RLanguageImpl(RLanguage.CALL, expr, null);
}
@Override
public @Nullable CombinedRElement findCombinedStruct(final RElementName symbol,
final @Nullable RObject env, final boolean inherits,
final int options, final int depth,
final ProgressMonitor m) throws StatusException {
if (!isValidSymbol(symbol)) {
throw new IllegalArgumentException("symbol"); //$NON-NLS-1$
}
final RObject[] data= this.fRjs.findData(symbol.getSegmentName(), env, inherits,
CombinedFactory.FACTORY_ID, (options | RObjectFactory.F_ONLY_STRUCT), depth,
m );
if (data != null) {
final BasicCombinedRElement element= checkCombinedRElement(data[0], options, symbol);
if (element != null) {
element.setParent(checkCombinedRElement(data[1], options,
(env instanceof CombinedRElement) ? ((CombinedRElement) env).getElementName() : null ));
return element;
}
}
return null;
}
@Override
public @Nullable CombinedRElement findCombinedStruct(final RElementName symbol,
final @Nullable RElementName envName, final boolean inherits,
final int options, final int depth,
final ProgressMonitor m) throws StatusException {
if (!isValidSymbol(symbol)) {
throw new IllegalArgumentException("symbol"); //$NON-NLS-1$
}
final RLanguage env= (envName != null) ? createLangObject(envName, "envName") : null;
final RObject[] data= this.fRjs.findData(symbol.getSegmentName(), env, inherits,
CombinedFactory.FACTORY_ID, (options | RObjectFactory.F_ONLY_STRUCT), depth,
m );
if (data != null) {
final BasicCombinedRElement element= checkCombinedRElement(data[0], options, symbol);
if (element != null) {
element.setParent(checkCombinedRElement(data[1], options, envName));
return element;
}
}
return null;
}
@Override
public void assignData(final String expression, final RObject data,
final ProgressMonitor m) throws StatusException {
this.fRjs.assignData(expression, data, null, m);
}
@Override
public void downloadFile(final OutputStream out, final String fileName, final int options,
final ProgressMonitor m) throws StatusException {
this.fRjs.downloadFile(out, fileName, options, m);
}
@Override
public byte[] downloadFile(final String fileName, final int options,
final ProgressMonitor m) throws StatusException {
return this.fRjs.downloadFile(fileName, options, m);
}
@Override
public void uploadFile(final InputStream in, final long length, final String fileName, final int options,
final ProgressMonitor m) throws StatusException {
this.fRjs.uploadFile(in, length, fileName, options, m);
}
@Override
public FunctionCall createFunctionCall(final String name) throws StatusException {
return new FunctionCallImpl(this.fRjs, name, this.fRObjectFactory);
}
@Override
public RGraphicCreator createRGraphicCreator(final int options) throws StatusException {
return new RGraphicCreatorImpl(this, this.fRjs, options);
}
@Override
public void addCancelHandler(final Callable<Boolean> handler) {
this.fRjs.addCancelHandler(handler);
}
@Override
public void removeCancelHandler(final Callable<Boolean> handler) {
this.fRjs.removeCancelHandler(handler);
}
@Override
public Lock getWaitLock() {
return this.fRjs.getWaitLock();
}
@Override
public void waitingForUser(final ProgressMonitor m) {
this.fRjs.waitingForUser();
}
@Override
public void resume() {
this.fRjs.resume();
}
}