blob: a9a6548e3ea4451b9e2e54c50f197a7418da6e06 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2009, 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.variables;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.ui.dialogs.SearchPattern;
import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
import org.eclipse.statet.jcommons.lang.NonNull;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.ecommons.ui.util.UIAccess;
import org.eclipse.statet.ltk.model.core.elements.IModelElement;
import org.eclipse.statet.r.apps.ui.RApp;
import org.eclipse.statet.r.apps.ui.RApp.AppEvent;
import org.eclipse.statet.r.apps.ui.VariablesData;
import org.eclipse.statet.r.console.core.RProcessREnvironment;
import org.eclipse.statet.r.core.data.CombinedRElement;
import org.eclipse.statet.r.ui.util.RNameSearchPattern;
@NonNullByDefault
class ContentJob extends Job implements RApp.Listener {
static class ContentFilter implements IModelElement.Filter {
private final boolean filterInternal;
private final @Nullable SearchPattern searchPattern;
public ContentFilter(final boolean filterInternal, final @Nullable SearchPattern pattern) {
this.filterInternal= filterInternal;
this.searchPattern= pattern;
}
@Override
public boolean include(final IModelElement element) {
final String name= element.getElementName().getSegmentName();
if (name != null) {
if (this.filterInternal && name.length() > 0 && name.charAt(0) == '.') {
return false;
}
return (this.searchPattern == null || this.searchPattern.matches(name));
}
else {
return true;
}
}
}
private final AppVarView view;
/** the app to update */
private @Nullable RApp updateSource;
/** the app of last update */
private @Nullable RApp lastSource;
/** update all environment */
private boolean force;
private @Nullable VariablesData rawInput;
private volatile boolean isScheduled;
public ContentJob(final AppVarView view) {
super("R Object Browser Update");
this.view= view;
setSystem(true);
setUser(false);
}
@Override
public void onVariablesChanged(final AppEvent event) {
schedule(event.getApp());
}
public void forceUpdate(final @Nullable RApp app) {
synchronized (this.view.sourceLock) {
if (app != this.view.getApp()) {
return;
}
this.updateSource= app;
this.force= true;
}
}
public void schedule(final RApp app) {
if (app != null) {
synchronized (this.view.sourceLock) {
if (app != this.view.getApp()) {
return;
}
this.updateSource= app;
}
}
schedule();
}
@Override
public boolean shouldSchedule() {
this.isScheduled= true;
return true;
}
@Override
protected IStatus run(final IProgressMonitor monitor) {
if (!this.isScheduled) {
return Status.CANCEL_STATUS;
}
final IWorkbenchSiteProgressService progressService= this.view.getViewSite().getService(IWorkbenchSiteProgressService.class);
if (progressService != null) {
progressService.incrementBusy();
}
try {
final RApp app;
final boolean sourceChanged;
final boolean updateInput;
synchronized (this.view.sourceLock) {
this.isScheduled= false;
app= this.view.getApp();
sourceChanged= (app != this.lastSource);
updateInput= (sourceChanged || this.updateSource != null);
this.lastSource= app;
this.updateSource= null;
this.force= false;
}
final AppVarInput input= (app != null) ? createInput(app) : null;
// Update input and refresh
final List<RProcessREnvironment> toUpdate;
if (updateInput) {
toUpdate= updateFromSource(input);
}
else {
toUpdate= null;
}
prepare(input);
synchronized (this.view.sourceLock) {
if (app != this.view.getApp()) {
this.lastSource= null;
return Status.CANCEL_STATUS;
}
if ((!sourceChanged && this.isScheduled) || monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
}
UIAccess.getDisplay().syncExec(() -> {
if (app != ContentJob.this.view.getApp()) {
return;
}
ContentJob.this.view.updateView(input, toUpdate);
});
return Status.OK_STATUS;
}
finally {
if (progressService != null) {
progressService.decrementBusy();
}
}
}
private AppVarInput createInput(final RApp source) {
final boolean filterInternal= !this.view.getFilterIncludeInternal();
final String filterText= this.view.getFilterSearchText();
IModelElement.Filter envFilter;
IModelElement.Filter otherFilter;
if (filterText != null && filterText.length() > 0) {
final SearchPattern filterPattern= new RNameSearchPattern();
filterPattern.setPattern(filterText);
envFilter= new ContentFilter(filterInternal, filterPattern);
otherFilter= (filterInternal) ? new ContentFilter(filterInternal, null) : null;
}
else if (filterInternal) {
envFilter= new ContentFilter(filterInternal, null);
otherFilter= new ContentFilter(filterInternal, null);
}
else {
envFilter= null;
otherFilter= null;
}
return new AppVarInput(source, envFilter, otherFilter);
}
private @Nullable List<RProcessREnvironment> updateFromSource(final @Nullable AppVarInput input) {
if (input == null) {
this.rawInput= null;
return null;
}
final RApp app= input.getSource();
final VariablesData oldInput= this.rawInput;
final VariablesData data= app.getVariables();
this.rawInput= data;
if (data != null) {
// If search path (environments) is not changed and not in force mode, refresh only the updated entries
final List<RProcessREnvironment> updateEntries= null;
return updateEntries;
}
else {
return null;
}
}
private void prepare(final @Nullable AppVarInput input) {
final VariablesData rawInput= this.rawInput;
if (input == null || rawInput == null) {
return;
}
@NonNull CombinedRElement[] array;
final List<? extends CombinedRElement> elements= rawInput.getElements();
if (elements != null) {
// if (elements.size() == 1) {
// ICombinedRElement single= elements.get(0);
// }
array= elements.toArray(new @NonNull CombinedRElement[elements.size()]);
if (input.hasEnvFilter()) { // prepare env filter
for (int i= 0; i < array.length; i++) {
input.getEnvChildren(array[i]);
}
}
}
else {
array= null;
}
input.setRootElements(array);
}
}