blob: 4af0b43691bbe8a0ddeb97df0cb89291b546f6b7 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2007, 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.debug.ui.preferences;
import static org.eclipse.statet.rj.renv.core.REnvConfiguration.R_HOME_DIRECTORY_VAR_STRING;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import com.ibm.icu.text.Collator;
import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.beans.typed.BeanProperties;
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
import org.eclipse.core.databinding.validation.ValidationStatus;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.variables.IStringVariableManager;
import org.eclipse.core.variables.VariablesPlugin;
import org.eclipse.jface.databinding.swt.typed.WidgetProperties;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.statushandlers.StatusManager;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.collections.ImList;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.jcommons.status.StatusException;
import org.eclipse.statet.ecommons.databinding.jface.DataBindingSupport;
import org.eclipse.statet.ecommons.debug.core.util.LaunchUtils;
import org.eclipse.statet.ecommons.debug.ui.util.ProcessOutputCollector;
import org.eclipse.statet.ecommons.io.FileUtil;
import org.eclipse.statet.ecommons.runtime.core.util.StatusUtils;
import org.eclipse.statet.ecommons.ui.components.ButtonGroup;
import org.eclipse.statet.ecommons.ui.components.DataAdapter;
import org.eclipse.statet.ecommons.ui.components.ExtensibleTextCellEditor;
import org.eclipse.statet.ecommons.ui.dialogs.ExtStatusDialog;
import org.eclipse.statet.ecommons.ui.util.LayoutUtils;
import org.eclipse.statet.ecommons.ui.util.MessageUtils;
import org.eclipse.statet.ecommons.ui.util.VariableFilterUtils;
import org.eclipse.statet.ecommons.ui.viewers.ViewerUtils;
import org.eclipse.statet.ecommons.ui.viewers.ViewerUtils.TreeComposite;
import org.eclipse.statet.ecommons.ui.workbench.ResourceInputComposite;
import org.eclipse.statet.internal.r.ui.RUIPlugin;
import org.eclipse.statet.internal.r.ui.help.IRUIHelpContextIds;
import org.eclipse.statet.r.core.RUtil;
import org.eclipse.statet.r.core.renv.IREnvConfiguration;
import org.eclipse.statet.r.core.renv.IREnvConfiguration.Exec;
import org.eclipse.statet.r.core.renv.RLibGroupWorkingCopy;
import org.eclipse.statet.r.ui.REnvLabelProvider;
import org.eclipse.statet.r.ui.RUI;
import org.eclipse.statet.rj.renv.core.BasicREnvConfiguration;
import org.eclipse.statet.rj.renv.core.DefaultLocalConfigurator;
import org.eclipse.statet.rj.renv.core.RLibGroup;
import org.eclipse.statet.rj.renv.core.RLibLocation;
/**
* Dialog for a local standard {@link IREnvConfiguration} (<code>user-local</code>)
*/
public class LocalREnvConfigDialog extends ExtStatusDialog {
private static final String DETECT_START= "_R-Path-And-Library-Configuration_"; //$NON-NLS-1$
private static final String DETECT_COMMAND= "cat('"+DETECT_START+"'," //$NON-NLS-1$ //$NON-NLS-2$
+ "Sys.getenv(\'R_HOME\')," //$NON-NLS-1$
+ "Sys.getenv(\'R_ARCH\')," //$NON-NLS-1$
+ "paste(.Library,collapse=.Platform$path.sep)," //$NON-NLS-1$
+ "paste(.Library.site,collapse=.Platform$path.sep)," //$NON-NLS-1$
+ "Sys.getenv('R_LIBS')," //$NON-NLS-1$
+ "Sys.getenv('R_LIBS_USER')," //$NON-NLS-1$
+ "R.home('doc')," //$NON-NLS-1$
+ "R.home('share')," //$NON-NLS-1$
+ "R.home('include')," //$NON-NLS-1$
+ "R.version$arch," //$NON-NLS-1$
+ ".Platform$OS.type," //$NON-NLS-1$
+ "sep=intToUtf8(0x0AL));"; //$NON-NLS-1$
private static final int DETECT_LENGTH= 12;
private static final int DETECT_R_HOME= 1;
private static final int DETECT_R_ARCHVAR= 2;
private static final int DETECT_R_DEFAULT= 3;
private static final int DETECT_R_SITE= 4;
private static final int DETECT_R_OTHER= 5;
private static final int DETECT_R_USER= 6;
private static final int DETECT_R_DOC_DIR= 7;
private static final int DETECT_R_SHARE_DIR= 8;
private static final int DETECT_R_INCLUDE_DIR= 9;
private static final int DETECT_R_ARCH= 10;
private static final int DETECT_R_OS= 11;
private static final Pattern DETECT_ITEM_PATTERN= RUtil.LINE_SEPARATOR_PATTERN;
private static final Pattern DETECT_PATH_PATTERN= Pattern.compile(File.pathSeparator, Pattern.LITERAL);
private class RHomeComposite extends ResourceInputComposite {
public RHomeComposite(final Composite parent) {
super (parent,
ResourceInputComposite.STYLE_TEXT,
ResourceInputComposite.MODE_DIRECTORY | ResourceInputComposite.MODE_OPEN,
Messages.REnv_Detail_Location_label);
setShowInsertVariable(true, VariableFilterUtils.DEFAULT_NON_ITERACTIVE_FILTERS, null);
}
@Override
protected void fillMenu(final Menu menu) {
super.fillMenu(menu);
final MenuItem item= new MenuItem(menu, SWT.PUSH);
item.setText(Messages.REnv_Detail_Location_FindAuto_label);
item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
final String[] rhome= searchRHOME();
if (rhome != null) {
setText(rhome[0]);
updateArchs(false);
final String current= LocalREnvConfigDialog.this.nameControl.getText().trim();
if ((current.isEmpty() || current.equals("R")) && rhome[1] != null) { //$NON-NLS-1$
LocalREnvConfigDialog.this.nameControl.setText(rhome[1]);
}
}
else {
final String name= Messages.REnv_Detail_Location_label;
MessageDialog.openInformation(getShell(),
MessageUtils.removeMnemonics(name),
NLS.bind(Messages.REnv_Detail_Location_FindAuto_Failed_message, name));
}
getTextControl().setFocus();
}
});
}
}
private static class RLibraryContainer {
private final RLibGroupWorkingCopy parent;
private RLibLocation library;
RLibraryContainer(final RLibGroupWorkingCopy parent, final RLibLocation library) {
this.parent= parent;
this.library= library;
}
public boolean set(final String directory) {
if (!this.library.getDirectory().equals(directory)) {
this.library= this.parent.setLibrary(this.library, directory);
return true;
}
return false;
}
@Override
public int hashCode() {
return this.library.hashCode();
}
@Override
public boolean equals(final @Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof RLibraryContainer) {
final RLibraryContainer other= (RLibraryContainer) obj;
return (this.library == other.library);
}
return false;
}
}
private final IREnvConfiguration.WorkingCopy configModel;
private final boolean isNewConfig;
private final Set<String> existingNames;
private Text nameControl;
private ResourceInputComposite rHomeControl;
private Button loadButton;
private Combo rArchControl;
private TreeViewer rLibrariesViewer;
private ButtonGroup<RLibLocation> rLibrariesButtons;
private ResourceInputComposite rDocDirectoryControl;
private ResourceInputComposite rShareDirectoryControl;
private ResourceInputComposite rIncludeDirectoryControl;
public LocalREnvConfigDialog(final Shell parent,
final IREnvConfiguration.WorkingCopy config, final boolean isNewConfig,
final Collection<IREnvConfiguration> existingConfigs) {
super(parent, WITH_RUNNABLE_CONTEXT | ((isNewConfig) ? WITH_DATABINDING_CONTEXT :
(WITH_DATABINDING_CONTEXT | SHOW_INITIAL_STATUS)) );
this.configModel= config;
this.isNewConfig= isNewConfig;
this.existingNames= new HashSet<>();
for (final IREnvConfiguration ec : existingConfigs) {
this.existingNames.add(ec.getName());
}
setTitle(this.isNewConfig ?
Messages.REnv_Detail_AddDialog_title :
Messages.REnv_Detail_Edit_Dialog_title );
}
@Override
public void create() {
super.create();
PlatformUI.getWorkbench().getHelpSystem().setHelp(getShell(), IRUIHelpContextIds.R_ENV);
}
@Override
protected Control createDialogArea(final Composite parent) {
final Composite area= new Composite(parent, SWT.NONE);
area.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
area.setLayout(LayoutUtils.newDialogGrid(2));
{ // Name:
final Label label= new Label(area, SWT.LEFT);
label.setText(Messages.REnv_Detail_Name_label+':');
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
final Text text= new Text(area, SWT.BORDER);
final GridData gd= new GridData(SWT.FILL, SWT.CENTER, true, false);
gd.widthHint= LayoutUtils.hintWidth(text, 60);
text.setLayoutData(gd);
text.setEditable(this.configModel.isEditable());
this.nameControl= text;
}
if (this.configModel.isEditable()) {
// Location:
final Label label= new Label(area, SWT.LEFT);
label.setText(Messages.REnv_Detail_Location_label+':');
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
this.rHomeControl= new RHomeComposite(area);
this.rHomeControl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
}
LayoutUtils.addSmallFiller(area, false);
{ // Architecture / Bits:
final Label label= new Label(area, SWT.LEFT);
label.setText(Messages.REnv_Detail_Arch_label+':');
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
final Composite composite= new Composite(area, SWT.NONE);
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
composite.setLayout(LayoutUtils.newCompositeGrid(3));
{ this.rArchControl= new Combo(composite, SWT.DROP_DOWN);
final GridData gd= new GridData(SWT.FILL, SWT.FILL, false, false);
gd.widthHint= LayoutUtils.hintWidth(this.rArchControl, 8);
this.rArchControl.setLayoutData(gd);
this.rArchControl.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
final int selectionIdx;
if (!LocalREnvConfigDialog.this.rArchControl.getListVisible()
&& (selectionIdx= LocalREnvConfigDialog.this.rArchControl.getSelectionIndex()) >= 0) {
final String item= LocalREnvConfigDialog.this.rArchControl.getItem(selectionIdx);
}
}
});
this.rArchControl.setEnabled(this.configModel.isEditable());
}
if (this.configModel.isEditable()) {
this.loadButton= new Button(composite, SWT.PUSH);
this.loadButton.setLayoutData(new GridData(SWT.RIGHT, SWT.FILL, true, false, 2, 1));
this.loadButton.setText(Messages.REnv_Detail_DetectSettings_label);
this.loadButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
detectSettings();
}
});
}
else {
LayoutUtils.addGDDummy(composite, true, 2);
}
}
{ // Libraries:
final Label label= new Label(area, SWT.LEFT);
label.setText(Messages.REnv_Detail_Libraries_label+":"); //$NON-NLS-1$
label.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
final Composite composite= new Composite(area, SWT.NONE);
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
composite.setLayout(LayoutUtils.newCompositeGrid(2));
final TreeComposite treeComposite= new ViewerUtils.TreeComposite(composite, SWT.BORDER | SWT.SINGLE | SWT.FULL_SELECTION);
this.rLibrariesViewer= treeComposite.viewer;
final GridData gd= new GridData(SWT.FILL, SWT.FILL, true, true);
gd.widthHint= LayoutUtils.hintWidth(this.nameControl, 80);
gd.heightHint= LayoutUtils.hintHeight(treeComposite.tree, 10);
treeComposite.setLayoutData(gd);
treeComposite.viewer.setContentProvider(new ITreeContentProvider() {
@Override
public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) {
}
@Override
public void dispose() {
}
@Override
public Object[] getElements(final Object inputElement) {
return LocalREnvConfigDialog.this.configModel.getRLibGroups().toArray();
}
@Override
public Object getParent(final Object element) {
if (element instanceof RLibraryContainer) {
return ((RLibraryContainer) element).parent;
}
return null;
}
@Override
public boolean hasChildren(final Object element) {
if (element instanceof RLibGroupWorkingCopy) {
return !((RLibGroupWorkingCopy) element).getLibLocations().isEmpty();
}
return false;
}
@Override
public Object[] getChildren(final Object parentElement) {
if (parentElement instanceof RLibGroupWorkingCopy) {
final RLibGroupWorkingCopy group= (RLibGroupWorkingCopy) parentElement;
final List<? extends RLibLocation> libs= group.getLibLocations();
final RLibraryContainer[] array= new RLibraryContainer[libs.size()];
for (int i= 0; i < libs.size(); i++) {
array[i]= new RLibraryContainer(group, libs.get(i));
}
return array;
}
return null;
}
});
final TreeViewerColumn column= treeComposite.addColumn(SWT.LEFT, new ColumnWeightData(100));
column.setLabelProvider(new REnvLabelProvider() {
@Override
public void update(final ViewerCell cell) {
final Object element= cell.getElement();
if (element instanceof RLibraryContainer) {
final RLibLocation lib= ((RLibraryContainer) element).library;
cell.setImage(RUI.getImage(RUI.IMG_OBJ_LIBRARY_LOCATION));
if (lib.getSource() != RLibLocation.USER && lib.getLabel() != null) {
cell.setText(lib.getLabel());
}
else {
cell.setText(lib.getDirectory());
}
finishUpdate(cell);
return;
}
super.update(cell);
}
});
column.setEditingSupport(new EditingSupport(treeComposite.viewer) {
@Override
protected boolean canEdit(final Object element) {
if (element instanceof RLibraryContainer) {
final RLibraryContainer container= ((RLibraryContainer) element);
return (container.library.getSource() == RLibLocation.USER);
}
return false;
}
@Override
protected void setValue(final Object element, final Object value) {
final RLibraryContainer container= (RLibraryContainer) element;
if (container.set((String) value)) {
getViewer().refresh(container, true);
getDataBinding().updateStatus();
}
}
@Override
protected Object getValue(final Object element) {
final RLibraryContainer container= (RLibraryContainer) element;
return container.library.getDirectory();
}
@Override
protected CellEditor getCellEditor(final Object element) {
return new ExtensibleTextCellEditor(treeComposite.tree) {
@Override
protected Control createCustomControl(final Composite parent) {
final ResourceInputComposite chooseResourceComposite= new ResourceInputComposite(parent,
ResourceInputComposite.STYLE_TEXT,
ResourceInputComposite.MODE_DIRECTORY | ResourceInputComposite.MODE_OPEN,
Messages.REnv_Detail_LibraryLocation_label) {
@Override
protected void beforeMenuAction() {
getFocusGroup().discontinueTracking();
}
@Override
protected void afterMenuAction() {
getFocusGroup().continueTracking();
}
};
chooseResourceComposite.setShowInsertVariable(true,
VariableFilterUtils.DEFAULT_NON_ITERACTIVE_FILTERS, null);
this.text= (Text) chooseResourceComposite.getTextControl();
return chooseResourceComposite;
}
};
}
});
treeComposite.viewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS);
treeComposite.viewer.setInput(this.configModel);
ViewerUtils.installDefaultEditBehaviour(treeComposite.viewer);
ViewerUtils.scheduleStandardSelection(treeComposite.viewer);
this.rLibrariesButtons= new ButtonGroup<RLibLocation>(composite) {
@Override
protected RLibLocation edit1(final RLibLocation item, final boolean newItem, final Object parent) {
if (newItem) {
return ((RLibGroupWorkingCopy) parent).newLibrary(""); //$NON-NLS-1$
}
return item;
}
@Override
public void updateState() {
super.updateState();
if (getDataAdapter().isDirty()) {
getDataBinding().updateStatus();
}
}
};
this.rLibrariesButtons.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true));
this.rLibrariesButtons.addAddButton(null);
this.rLibrariesButtons.addDeleteButton(null);
// this.rLibrariesButtons.addSeparator();
// this.rLibrariesButtons.addUpButton();
// this.rLibrariesButtons.addDownButton();
final DataAdapter<RLibLocation> adapter= new DataAdapter.TreeAdapter<RLibLocation>(
(ITreeContentProvider) this.rLibrariesViewer.getContentProvider(), null ) {
private RLibGroupWorkingCopy getGroup(final Object element) {
if (element instanceof RLibGroupWorkingCopy) {
return (RLibGroupWorkingCopy) element;
}
else {
return ((RLibraryContainer) element).parent;
}
}
@Override
public RLibLocation getModelItem(final Object element) {
if (element instanceof RLibraryContainer) {
return ((RLibraryContainer) element).library;
}
return (RLibLocation) element;
}
@Override
public Object getViewerElement(final RLibLocation item, final Object parent) {
return new RLibraryContainer((RLibGroupWorkingCopy) parent, item);
}
@Override
public boolean isAddAllowed(final Object element) {
return !getGroup(element).getId().equals(RLibGroup.R_DEFAULT);
}
@Override
public boolean isModifyAllowed(final Object element) {
return ( element instanceof RLibraryContainer
&& ((RLibraryContainer) element).library.getSource() == RLibLocation.USER );
}
@Override
public Object getAddParent(final Object element) {
return getGroup(element);
}
@Override
public List<? extends RLibLocation> getContainerFor(final Object element) {
if (element instanceof RLibGroup) {
return ((RLibGroup) element).getLibLocations();
}
else {
return ((RLibraryContainer) element).parent.getLibLocations();
}
}
};
this.rLibrariesButtons.connectTo(this.rLibrariesViewer, adapter);
}
if (this.configModel.isEditable()) {
final Composite group= createInstallDirGroup(area);
if (group != null) {
group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
}
}
LayoutUtils.addSmallFiller(area, true);
applyDialogFont(area);
return area;
}
private Composite createInstallDirGroup(final Composite parent) {
final Group composite= new Group(parent, SWT.NONE);
composite.setLayout(LayoutUtils.newGroupGrid(2));
composite.setText("Advanced - Installation locations:");
{ final Label label= new Label(composite, SWT.NONE);
label.setText("Documentation ('R_DOC_DIR'):");
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
final ResourceInputComposite text= new ResourceInputComposite(composite, ResourceInputComposite.STYLE_TEXT,
(ResourceInputComposite.MODE_DIRECTORY | ResourceInputComposite.MODE_OPEN), "R_DOC_DIR"); //$NON-NLS-1$
text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
text.setShowInsertVariable(true, VariableFilterUtils.DEFAULT_NON_ITERACTIVE_FILTERS, null);
this.rDocDirectoryControl= text;
}
{ final Label label= new Label(composite, SWT.NONE);
label.setText("Shared files ('R_SHARE_DIR'):");
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
final ResourceInputComposite text= new ResourceInputComposite(composite, ResourceInputComposite.STYLE_TEXT,
(ResourceInputComposite.MODE_DIRECTORY | ResourceInputComposite.MODE_OPEN), "R_SHARE_DIR"); //$NON-NLS-1$
text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
text.setShowInsertVariable(true, VariableFilterUtils.DEFAULT_NON_ITERACTIVE_FILTERS, null);
this.rShareDirectoryControl= text;
}
{ final Label label= new Label(composite, SWT.NONE);
label.setText("Include files ('R_INCLUDE_DIR'):");
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
final ResourceInputComposite text= new ResourceInputComposite(composite, ResourceInputComposite.STYLE_TEXT,
(ResourceInputComposite.MODE_DIRECTORY | ResourceInputComposite.MODE_OPEN), "R_INCLUDE_DIR"); //$NON-NLS-1$
text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
text.setShowInsertVariable(true, VariableFilterUtils.DEFAULT_NON_ITERACTIVE_FILTERS, null);
this.rIncludeDirectoryControl= text;
}
return composite;
}
@Override
protected void addBindings(final DataBindingSupport db) {
// don't specify IREnvConfiguration.WorkingCopy for BeanProperties (missing with getter)
db.getContext().bindValue(
WidgetProperties.text(SWT.Modify)
.observe(this.nameControl),
BeanProperties.value(IREnvConfiguration.PROP_NAME, String.class)
.observe(this.configModel),
new UpdateValueStrategy<String, String>()
.setAfterGetValidator((final String value) -> {
final String s= value.trim();
if (s.isEmpty()) {
return ValidationStatus.error(Messages.REnv_Detail_Name_error_Missing_message);
}
if (LocalREnvConfigDialog.this.existingNames.contains(s)) {
return ValidationStatus.error(Messages.REnv_Detail_Name_error_Duplicate_message);
}
if (s.contains("/")) { //$NON-NLS-1$
return ValidationStatus.error(Messages.REnv_Detail_Name_error_InvalidChar_message);
}
return ValidationStatus.ok();
}),
null );
if (this.rHomeControl != null) {
final Binding rHomeBinding= db.getContext().bindValue(
this.rHomeControl.getObservable(),
BeanProperties.value(IREnvConfiguration.PROP_RHOME, String.class)
.observe(this.configModel),
new UpdateValueStrategy<String, String>()
.setAfterGetValidator((final String value) -> {
final IStatus status= LocalREnvConfigDialog.this.rHomeControl.getValidator().validate(value);
if (!status.isOK()) {
return status;
}
if (!LocalREnvConfigDialog.this.configModel.isValidRHomeLocation(LocalREnvConfigDialog.this.rHomeControl.getResourceAsFileStore())) {
return ValidationStatus.error(Messages.REnv_Detail_Location_error_NoRHome_message);
}
updateArchs(!LocalREnvConfigDialog.this.isNewConfig);
return ValidationStatus.ok();
}),
null );
rHomeBinding.getValidationStatus().addValueChangeListener(new IValueChangeListener<IStatus>() {
@Override
public void handleValueChange(final ValueChangeEvent<? extends IStatus> event) {
final IStatus status= event.diff.getNewValue();
LocalREnvConfigDialog.this.loadButton.setEnabled(status.isOK());
}
});
rHomeBinding.validateTargetToModel();
}
db.getContext().bindValue(
WidgetProperties.text()
.observe(this.rArchControl),
BeanProperties.value(IREnvConfiguration.PROP_SUBARCH, String.class)
.observe(this.configModel) );
if (this.rDocDirectoryControl != null) {
db.getContext().bindValue(
this.rDocDirectoryControl.getObservable(),
BeanProperties.value(IREnvConfiguration.PROP_RDOC_DIRECTORY, String.class)
.observe(this.configModel) );
db.getContext().bindValue(
this.rShareDirectoryControl.getObservable(),
BeanProperties.value(IREnvConfiguration.PROP_RSHARE_DIRECTORY, String.class)
.observe(this.configModel) );
db.getContext().bindValue(
this.rIncludeDirectoryControl.getObservable(),
BeanProperties.value(IREnvConfiguration.PROP_RINCLUDE_DIRECTORY, String.class)
.observe(this.configModel) );
}
}
private String[] searchRHOME() {
try {
final IStringVariableManager variables= VariablesPlugin.getDefault().getStringVariableManager();
{ String loc= variables.performStringSubstitution("${env_var:R_HOME}", false); //$NON-NLS-1$
if (loc != null && loc.length() > 0) {
loc= resolve(loc);
if (loc != null) {
final IFileStore locStore= EFS.getLocalFileSystem().getStore(new Path(loc));
if (locStore.fetchInfo().exists()) {
return new String[] { loc, Messages.REnv_SystemRHome_name };
}
}
}
}
final ImList<String> locCandidates;
String prefixPattern= null;
String prefixReplacement= null;
if (Platform.getOS().startsWith("win")) { //$NON-NLS-1$
String baseLoc= "${env_var:PROGRAMFILES}\\R"; //$NON-NLS-1$
final IFileStore baseStore= EFS.getLocalFileSystem().getStore(
new Path(variables.performStringSubstitution(baseLoc)));
if (baseStore.fetchInfo().exists()) {
prefixReplacement= baseLoc;
prefixPattern= baseLoc= FileUtil.toString(baseStore);
final String[] names= baseStore.childNames(EFS.NONE, null);
Arrays.sort(names, 0, names.length,
Collections.reverseOrder(Collator.getInstance()) );
for (int i= 0; i < names.length; i++) {
names[i]= baseLoc + '\\' + names[i];
}
locCandidates= ImCollections.newList(names);
}
else {
locCandidates= ImCollections.newList();
}
}
else if (Platform.getOS().equals(Platform.OS_MACOSX)) {
locCandidates= ImCollections.newList(
"/Library/Frameworks/R.framework/Resources" //$NON-NLS-1$
);
}
else {
locCandidates= ImCollections.newList(
"/usr/local/lib64/R", //$NON-NLS-1$
"/usr/lib64/R", //$NON-NLS-1$
"/usr/local/lib/R", //$NON-NLS-1$
"/usr/lib/R" //$NON-NLS-1$
);
}
for (String loc : locCandidates) {
loc= resolve(loc);
if (loc != null) {
final IFileStore locStore= EFS.getLocalFileSystem().getStore(new Path(loc));
if (this.configModel.isValidRHomeLocation(locStore)) {
if (prefixPattern != null && loc.startsWith(prefixPattern)) {
loc= prefixReplacement + loc.substring(prefixPattern.length());
}
String name= locStore.getName();
if (name.equals("Resources")) { //$NON-NLS-1$
final IFileStore parent= locStore.getParent();
name= (parent != null) ? parent.getName() : null;
}
if (name != null) {
if (name.isEmpty() || name.equals("R")) { //$NON-NLS-1$
name= null;
}
else if (Character.isDigit(name.charAt(0))) {
name= "R " + name; //$NON-NLS-1$
}
}
return new String[] { loc, name };
}
}
}
return null;
}
catch (final Exception e) {
RUIPlugin.logError(-1, "Error when searching R_HOME location", e); //$NON-NLS-1$
return null;
}
}
private String resolve(final String loc) {
try {
java.nio.file.Path path= Paths.get(loc);
path= path.toRealPath();
return path.toString();
}
catch (final IOException e2) {
return null;
}
}
private void updateArchs(final boolean conservative) {
if (this.rHomeControl == null) {
return;
}
try {
final IFileStore rHome= this.rHomeControl.getResourceAsFileStore();
final List<String> availableArchs= this.configModel.searchAvailableSubArchs(rHome);
if (availableArchs == null) {
this.rArchControl.setItems(new String[0]);
return;
}
final String oldArch= this.rArchControl.getText();
this.rArchControl.setItems(availableArchs.toArray(new String[availableArchs.size()]));
int idx= (oldArch.length() > 0) ? availableArchs.indexOf(oldArch) : -1;
if (idx >= 0) {
this.rArchControl.select(idx);
}
if (conservative && this.rArchControl.getText().length() > 0) {
return;
}
idx= availableArchs.indexOf(Platform.getOSArch());
if (idx < 0) {
if (Platform.getOSArch().equals(Platform.ARCH_X86)) {
idx= availableArchs.indexOf("i386"); //$NON-NLS-1$
if (idx < 0) {
idx= availableArchs.indexOf("i586"); //$NON-NLS-1$
if (idx < 0) {
idx= availableArchs.indexOf("i686"); //$NON-NLS-1$
}
}
}
}
if (idx < 0) {
idx= 0;
}
this.rArchControl.select(idx);
}
catch (final Exception e) {
this.rArchControl.setItems(new String[0]);
}
}
private void detectSettings() {
try {
run(true, true, new IRunnableWithProgress() {
@Override
public void run(final IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
detectSettings(monitor);
}
catch (final CoreException e) {
throw new InvocationTargetException(e);
}
}
});
}
catch (final InvocationTargetException e) {
final String message= (e.getCause() instanceof CoreException) ?
Messages.REnv_Detail_DetectSettings_error_message :
Messages.REnv_Detail_DetectSettings_error_Unexpected_message;
StatusManager.getManager().handle(new Status(IStatus.ERROR, RUI.BUNDLE_ID, -1,
message, e), StatusManager.LOG);
StatusManager.getManager().handle(new Status(IStatus.ERROR, RUI.BUNDLE_ID, -1,
message, e.getCause()), StatusManager.SHOW);
}
catch (final InterruptedException e) {
}
this.rLibrariesButtons.refresh();
this.rLibrariesViewer.expandAll();
}
private void detectSettings(final IProgressMonitor monitor) throws CoreException {
monitor.beginTask(Messages.REnv_Detail_DetectSettings_task, 10);
final ProcessBuilder processBuilder= new ProcessBuilder(this.configModel.getExecCommand(Exec.TERM));
processBuilder.command().add("--no-save"); //$NON-NLS-1$
processBuilder.command().add("--slave"); //$NON-NLS-1$
processBuilder.command().add("-e"); //$NON-NLS-1$
processBuilder.command().add(DETECT_COMMAND);
final Map<String, String> envp= processBuilder.environment();
try {
final DefaultLocalConfigurator setup= new DefaultLocalConfigurator(this.configModel);
LaunchUtils.configureEnvironment(envp, null,
setup.getEnvironmentsVariables(DefaultLocalConfigurator.MINIMAL_SETUP) );
}
catch (final StatusException e) {
throw StatusUtils.convert(e);
}
monitor.worked(1);
final ProcessOutputCollector reader= new ProcessOutputCollector(processBuilder, "'Detect R settings'", monitor); //$NON-NLS-1$
final String output= reader.collect();
final int start= output.indexOf(DETECT_START);
if (start >= 0) {
final String[] lines= DETECT_ITEM_PATTERN.split(output.substring(start));
if (lines.length == DETECT_LENGTH) {
updateLibraries(this.configModel.getRLibGroup(RLibGroup.R_DEFAULT),
lines[DETECT_R_DEFAULT], lines[DETECT_R_HOME]);
final RLibGroupWorkingCopy group= this.configModel.getRLibGroup(RLibGroup.R_SITE);
updateLibraries(group, lines[DETECT_R_SITE], lines[DETECT_R_HOME]);
if (group.getLibLocations().isEmpty()) {
group.getLibLocations().add(group.newLibrary(BasicREnvConfiguration.DEFAULT_R_SITE_LOCATION_DIRECTORY));
}
updateLibraries(this.configModel.getRLibGroup(RLibGroup.R_OTHER),
lines[DETECT_R_OTHER], lines[DETECT_R_HOME]);
updateLibraries(this.configModel.getRLibGroup(RLibGroup.R_USER),
lines[DETECT_R_USER], lines[DETECT_R_HOME]);
this.configModel.setRDocDirectory(checkDir(lines[DETECT_R_DOC_DIR], lines[DETECT_R_HOME]));
this.configModel.setRShareDirectory(checkDir(lines[DETECT_R_SHARE_DIR], lines[DETECT_R_HOME]));
this.configModel.setRIncludeDirectory(checkDir(lines[DETECT_R_INCLUDE_DIR], lines[DETECT_R_HOME]));
if (lines[DETECT_R_ARCHVAR].length() > 0) {
this.configModel.setRArch(lines[DETECT_R_ARCHVAR]);
}
else if (lines[DETECT_R_ARCH].length() > 0 && this.configModel.getRArch() == null) {
this.configModel.setRArch(lines[DETECT_R_ARCH]);
}
this.configModel.setROS(lines[DETECT_R_OS]);
return;
}
}
throw new CoreException(new Status(IStatus.ERROR, RUI.BUNDLE_ID, -1,
"Unexpected output:\n" + output, null)); //$NON-NLS-1$
}
private void updateLibraries(final RLibGroupWorkingCopy group, final String var, final String rHome) {
final List<RLibLocation> libraries= group.getLibLocations();
libraries.clear();
final String[] locations= DETECT_PATH_PATTERN.split(var);
final IPath rHomePath= new Path(rHome);
final IPath userHomePath= new Path(System.getProperty("user.home")); //$NON-NLS-1$
for (final String location : locations) {
if (location.isEmpty()) {
continue;
}
String s;
final IPath path;
if (location.startsWith("~/")) { //$NON-NLS-1$
path= userHomePath.append(location.substring(2));
}
else {
path= new Path(location);
}
if (rHomePath.isPrefixOf(path)) {
s= R_HOME_DIRECTORY_VAR_STRING + '/' + path.makeRelativeTo(rHomePath).toString();
}
else if (userHomePath.isPrefixOf(path)) {
s= "${user_home}/" + path.makeRelativeTo(userHomePath).toString(); //$NON-NLS-1$
}
else {
s= path.toString();
}
libraries.add(group.newLibrary(s));
}
}
private String checkDir(String dir, final String rHome) {
if (dir != null && dir.length() > 0) {
final IPath rHomePath= new Path(rHome);
final IPath path= new Path(dir);
if (rHomePath.isPrefixOf(path)) {
dir= R_HOME_DIRECTORY_VAR_STRING + '/' + path.makeRelativeTo(rHomePath).toString();
}
return dir;
}
return null;
}
}