blob: 7492cbc0331b688fa3e9a502ad343e2ab914e43a [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2017, 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.internal.r.apps.ui.launching;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.ObjectUtils;
import org.eclipse.statet.jcommons.lang.ObjectUtils.ToStringBuilder;
import org.eclipse.statet.jcommons.status.ErrorStatus;
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.Statuses;
import org.eclipse.statet.jcommons.ts.core.SystemRunnable;
import org.eclipse.statet.jcommons.ts.core.Tool;
import org.eclipse.statet.jcommons.ts.core.ToolQueue;
import org.eclipse.statet.internal.r.apps.ui.RAppUIPlugin;
import org.eclipse.statet.nico.core.runtime.Queue;
import org.eclipse.statet.r.apps.ui.VariablesData;
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.RElementName;
import org.eclipse.statet.r.core.tool.TmpUtils;
import org.eclipse.statet.r.nico.ICombinedRDataAdapter;
import org.eclipse.statet.rj.data.RDataUtils;
import org.eclipse.statet.rj.data.RObject;
import org.eclipse.statet.rj.data.RReference;
import org.eclipse.statet.rj.data.UnexpectedRDataException;
import org.eclipse.statet.rj.services.RService;
import org.eclipse.statet.rj.ts.core.AbstractRToolRunnable;
import org.eclipse.statet.rj.ts.core.RToolService;
@NonNullByDefault
public class DataLoader extends AbstractRToolRunnable implements SystemRunnable {
public static final String TASK_ID= "org.eclipse.statet.r.apps/LoadVariables"; //$NON-NLS-1$
private final AppRunner app;
private final String expression;
private final Job scheduleJob;
private final AtomicInteger scheduledRetry= new AtomicInteger(0);
private RWorkspace workspace;
private final Set<Long> envs= new HashSet<>();
@SuppressWarnings("null")
public DataLoader(final AppRunner app, final String expression) {
super(TASK_ID, "Load elements of R app variables");
this.app= app;
this.expression= expression;
this.scheduleJob= new Job("Schedule R App Data") {
@Override
protected IStatus run(final IProgressMonitor monitor) {
scheduleHot();
return org.eclipse.core.runtime.Status.OK_STATUS;
}
};
this.scheduleJob.setSystem(true);
this.scheduleJob.setPriority(Job.SHORT);
final Queue queue= getTool().getQueue();
queue.addOnIdle(this, 5100);
}
@SuppressWarnings("null")
private RProcess getTool() {
return this.app.getTool();
}
public void schedule() {
this.scheduledRetry.set(0);
final long started= this.app.getStartedTimestamp();
if (started != Long.MIN_VALUE) {
final long diff= (System.nanoTime() - started) / 100000L;
if (diff < 100) {
this.scheduleJob.schedule(diff + 50);
return;
}
}
scheduleHot();
}
private void scheduleHot() {
final Queue queue= getTool().getQueue();
queue.addHot(this, Queue.IF_ABSENT);
}
public void stop() {
final Queue queue= getTool().getQueue();
queue.removeHot(this);
queue.removeOnIdle(this);
}
@Override
public boolean canRunIn(final Tool tool) {
return (tool == this.app.getTool());
}
@Override
protected void run(final RToolService r, final ProgressMonitor m) throws StatusException {
if (!this.app.isRunning()) {
return;
}
try {
final TmpUtils.Item tmpItem= TmpUtils.newItem("appvars", r, m); //$NON-NLS-1$
try {
evalExpression(tmpItem, (ICombinedRDataAdapter) r, m);
}
finally {
tmpItem.disposeChecked(m);
}
}
catch (final StatusException | UnexpectedRDataException e) {
final ToStringBuilder sb= new ObjectUtils.ToStringBuilder(
"An error occurred when evaluating the app variables expression." );
sb.addProp("expression", this.expression); //$NON-NLS-1$
RAppUIPlugin.logError(sb.toString(), e);
this.app.setData(new VariablesData(this.expression,
new ErrorStatus(RAppUIPlugin.BUNDLE_ID, 0,
"Evaluation failed (internal error).", e )));
}
finally {
this.envs.clear();
final ToolQueue queue= r.getTool().getQueue();
queue.removeHot(this);
}
}
private void evalExpression(final TmpUtils.Item tmpItem, final ICombinedRDataAdapter r,
final ProgressMonitor m) throws UnexpectedRDataException, StatusException {
final String valueName= tmpItem.createSub("value"); //$NON-NLS-1$
final RElementName valueElementName= RElementName.create(RElementName.MAIN_DEFAULT,
valueName );
final RElementName fqElementName= TmpUtils.createFQElementName(valueElementName);
CombinedRElement element= null;
try {
// System.out.println("App Data Update= " + r.getChangeStamp());
// int nframes= RDataUtil.checkSingleIntValue(r.evalData("sys.nframe()", m));
// System.out.println("nframes= " + nframes);
// for (int i= 0; i < nframes + 1; i++) {
// String s= "evalq(shiny::getDefaultReactiveDomain(), envir=sys.frame(" + i + "L))";
// System.out.println("expr= " + s);
//
// element= r.evalCombinedStruct(s,
// 0, RService.DEPTH_REFERENCE, fqElementName, m );
//
// System.out.println(((RReference)element).getRClassName());
// }
element= r.evalCombinedStruct(this.expression,
0, RService.DEPTH_REFERENCE, fqElementName, m );
}
catch (final StatusException e) {
final Status status= e.getStatus();
this.app.setData(new VariablesData(this.expression,
Statuses.newStatus(status.getSeverity(), RAppUIPlugin.BUNDLE_ID, 0,
"Evaluation failed: " + e.getLocalizedMessage(), e )));
return;
}
final RReference ref= RDataUtils.checkRReference(element);
if (ref.getReferencedRObjectType() == RObject.TYPE_NULL) {
if (this.scheduledRetry.incrementAndGet() <= 2
&& r.getController().getHotTasksState() != 0) {
this.scheduleJob.schedule(200);
return;
}
}
else {
this.scheduledRetry.set(0);
}
tmpItem.set(valueName, ref, m);
element= r.getWorkspaceData().resolve(ref,
RWorkspace.RESOLVE_UPTODATE | RWorkspace.RESOLVE_RECURSIVE, 0, m);
if (element == null) {
element= r.findCombinedStruct(valueElementName, TmpUtils.ENV_FQ_ELEMENT_NAME, false,
0, RService.DEPTH_INFINITE, m );
if (element == null) {
throw new UnexpectedRDataException("null"); //$NON-NLS-1$
}
}
RElementName name= RElementName.parseDefault(this.expression);
if (name == null) {
name= RElementName.create(RElementName.MAIN_OTHER, "variables");
}
element= ICombinedRDataAdapter.createReference(element, name);
this.app.setData(new VariablesData(this.expression,
ImCollections.newList(element) ));
}
// private void check(final ICombinedRElement element,
// final SubMonitor m) throws StatusException {
// if (element instanceof ICombinedRList) {
// check((ICombinedRList) element, m);
// }
// }
//
// private void resolve(final RReference ref, final @Nullable RElementName fullName,
// final int loadOptions, final SubMonitor m) throws StatusException {
// if (!this.envs.add(ref.getHandle())) {
// return;
// }
// final ICombinedRElement element= this.workspace.resolve(ref,
// RWorkspace.RESOLVE_UPTODATE, loadOptions, m );
// check(element, m);
// }
//
// private void check(final ICombinedRList list,
// final SubMonitor m) throws StatusException {
// if (list.hasModelChildren(null)) {
// final long length= list.getLength();
// if (length <= Integer.MAX_VALUE) {
// final int l= (int) length;
// ITER_CHILDREN : for (int i= 0; i < l; i++) {
// final RObject object= list.get(i);
// if (object != null) {
// switch (object.getRObjectType()) {
// case RObject.TYPE_REFERENCE:
// if (((RReference) object).getReferencedRObjectType() == RObject.TYPE_ENV) {
// resolve((RReference) object, null, 0, m);
// }
// else {
// }
// continue ITER_CHILDREN;
// case RObject.TYPE_LIST:
// case RObject.TYPE_S4OBJECT:
// check((ICombinedRList) object, m);
// continue ITER_CHILDREN;
// default:
// continue ITER_CHILDREN;
// }
// }
// }
// }
// else {
// ITER_CHILDREN : for (long i= 0; i < length; i++) {
// final RObject object= list.get(i);
// if (object != null) {
// switch (object.getRObjectType()) {
// case RObject.TYPE_REFERENCE:
// if (((RReference) object).getReferencedRObjectType() == RObject.TYPE_ENV) {
// resolve((RReference) object, null, 0, m);
// }
// else {
// }
// continue ITER_CHILDREN;
// case RObject.TYPE_LIST:
// case RObject.TYPE_S4OBJECT:
// check((ICombinedRList) object, m);
// continue ITER_CHILDREN;
// default:
// continue ITER_CHILDREN;
// }
// }
// }
// }
// }
// }
}