blob: 162b6639b8479dbfa72ff1cc8628b4de373dde46 [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.internal.r.console.ui.launching;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.jdt.debug.ui.launchConfigurations.JavaJRETab;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.IVMInstall3;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.VMStandin;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
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.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.collections.ImIdentitySet;
import org.eclipse.statet.ecommons.debug.ui.config.InputArgumentsComposite;
import org.eclipse.statet.ecommons.debug.ui.config.LaunchConfigUtils;
import org.eclipse.statet.ecommons.preferences.core.PreferenceSetService;
import org.eclipse.statet.ecommons.preferences.core.util.PreferenceUtils;
import org.eclipse.statet.ecommons.ui.util.LayoutUtils;
import org.eclipse.statet.ecommons.ui.util.UIAccess;
import org.eclipse.statet.internal.r.console.ui.Messages;
import org.eclipse.statet.internal.r.console.ui.RConsoleUIPlugin;
import org.eclipse.statet.r.core.renv.IREnvManager;
import org.eclipse.statet.r.launching.ui.REnvTab;
import org.eclipse.statet.rj.renv.core.REnv;
import org.eclipse.statet.rj.renv.core.REnvConfiguration;
/**
* Adds:
* <li>Optional requirement/validation of JRE</li>
* <li>VM Arguments</li>
*/
class ExtJavaJRETab extends JavaJRETab implements PreferenceSetService.ChangeListener {
private boolean is32(final String arch) {
return (arch.equals(Platform.ARCH_X86)
|| arch.equals("i386") || arch.equals("i586") || arch.equals("i686") ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
private boolean is64(final String arch) {
return (arch.equals(Platform.ARCH_X86_64)
|| arch.equals("amd64") ); //$NON-NLS-1$
}
private final ImIdentitySet<String> PREF_QUALIFIERS= ImCollections.newIdentitySet(
IREnvManager.PREF_QUALIFIER );
private final RConsoleMainTab mainTab;
private final REnvTab rEnvTab;
private InputArgumentsComposite vmArgsControl;
private final boolean enableVMArchCheck;
private IVMInstall lastCheckedVM;
private int lastCheckedVMBits;
private int lastCheckedRBits;
private boolean validInBackground;
public ExtJavaJRETab(final RConsoleMainTab mainTab, final REnvTab renvTab) {
this.mainTab= mainTab;
this.rEnvTab= renvTab;
PreferenceUtils.getInstancePrefs().addPreferenceSetListener(this, this.PREF_QUALIFIERS);
this.enableVMArchCheck= ((Platform.getOS().startsWith("win") || Platform.getOS().equals(Platform.OS_LINUX)) //$NON-NLS-1$
&& (Platform.getOSArch().startsWith("x86") || Platform.getOSArch().startsWith("amd64")) ); //$NON-NLS-1$ //$NON-NLS-2$
}
@Override
public void createControl(final Composite parent) {
super.createControl(parent);
final Composite tabHolder= getDynamicTabHolder();
final Composite composite= tabHolder.getParent();
final GridLayout layout= (GridLayout) composite.getLayout();
tabHolder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, layout.numColumns, 1));
final Composite extComposite= new Composite(composite, SWT.NONE);
final GridLayout extLayout= new GridLayout();
extLayout.marginHeight= 0;
extComposite.setLayout(extLayout);
extComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, layout.numColumns, 1));
final Group group= new Group(extComposite, SWT.NONE);
group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
group.setLayout(LayoutUtils.newGroupGrid(1));
group.setText(Messages.JavaJRE_Tab_VMConfig_group);
this.vmArgsControl= new InputArgumentsComposite(group, Messages.JavaJRE_Tab_VMArguments_label);
this.vmArgsControl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
this.vmArgsControl.getTextControl().addModifyListener(new ModifyListener() {
@Override
public void modifyText(final ModifyEvent e) {
updateLaunchConfigurationDialog();
}
});
final Label note= new Label(group, SWT.WRAP);
note.setText(this.vmArgsControl.getNoteText());
note.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
Dialog.applyDialogFont(extComposite);
}
@Override
protected void loadDynamicJREArea() {
super.loadDynamicJREArea();
final Composite tabHolder= getDynamicTabHolder();
tabHolder.getParent().layout(new Control[] { tabHolder });
}
@Override
public void preferenceChanged(final PreferenceSetService.ChangeEvent event) {
if (event.contains(IREnvManager.PREF_QUALIFIER)) {
UIAccess.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
final int previous= ExtJavaJRETab.this.lastCheckedRBits;
updateRBits();
if (previous != ExtJavaJRETab.this.lastCheckedRBits
&& UIAccess.isOkToUse(getControl())
&& LaunchConfigUtils.isActiveTabGroup(getLaunchConfigurationDialog(), ExtJavaJRETab.this)) {
getLaunchConfigurationDialog().updateMessage();
getLaunchConfigurationDialog().updateButtons();
}
}
});
}
}
@Override
public void dispose() {
PreferenceUtils.getInstancePrefs().removePreferenceSetListener(this);
super.dispose();
}
@Override
public boolean isValid(final ILaunchConfiguration config) {
if (this.enableVMArchCheck) {
if (this.validInBackground) {
scheduleUpdateJob();
return false;
}
final RConsoleType type= this.mainTab.getSelectedType();
if (type == null || !type.requireJRE()) {
setErrorMessage(null);
setMessage(null);
return true;
}
if (!super.isValid(config)) {
return false;
}
// try to check compatibility
updateRBits();
if (this.lastCheckedRBits == -1) {
// check not necessary
return true;
}
IVMInstall jre= null;
try {
jre= JavaRuntime.computeVMInstall(config);
}
catch (final CoreException e) {
}
if (jre instanceof VMStandin) {
jre= ((VMStandin) jre).convertToRealVM();
}
if (jre != null && this.lastCheckedVM != jre) {
this.lastCheckedVM= jre;
updateVMBits();
}
if (jre == null || this.lastCheckedVMBits == -1) {
// check failed technically
return true;
}
if (this.lastCheckedVMBits == this.lastCheckedRBits) {
return true;
}
else {
setErrorMessage(NLS.bind(Messages.JavaJRE_RCompatibility_error_DifferentBits_message, this.lastCheckedRBits, this.lastCheckedVMBits));
return false;
}
}
return true;
}
private void updateRBits() {
this.lastCheckedRBits= -1;
final REnv rEnv= this.rEnvTab.getSelectedEnv();
if (rEnv == null) {
return;
}
final REnvConfiguration rEnvConfig= rEnv.get(REnvConfiguration.class);
if (rEnvConfig == null) {
return;
}
final String arch= rEnvConfig.getRArch();
if (arch != null && arch.length() > 0) {
if (is32(arch)) {
this.lastCheckedRBits= 32;
}
else if (is64(arch)) {
this.lastCheckedRBits= 64;
}
}
}
private void updateVMBits() {
this.lastCheckedVMBits= -1;
if (this.lastCheckedVM instanceof IVMInstall3) {
this.validInBackground= true;
try {
getLaunchConfigurationDialog().run(true, true, new IRunnableWithProgress() {
@Override
public void run(final IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
final String[] propertyNames= new String[] { "os.arch", "java.vm.name", "sun.arch.data.model", "com.ibm.vm.bitmode" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
try {
final Map<String, String> properties= ((IVMInstall3) ExtJavaJRETab.this.lastCheckedVM).evaluateSystemProperties(propertyNames, monitor);
{ // try known os.arch
final String p= properties.get("os.arch"); //$NON-NLS-1$
if (p != null && p.length() > 0) {
if (is32(p)) {
ExtJavaJRETab.this.lastCheckedVMBits= 32;
return;
}
else if (is64(p)) {
ExtJavaJRETab.this.lastCheckedVMBits= 64;
return;
}
}
}
}
catch (final CoreException e) {
throw new InvocationTargetException(e);
}
}
});
}
catch (final InvocationTargetException e) {
RConsoleUIPlugin.logError(
"An error when trying to fetch VM properties for JRE validation.", //$NON-NLS-1$
e.getTargetException() );
}
catch (final InterruptedException e) {
}
finally {
this.validInBackground= false;
}
}
}
@Override
public void initializeFrom(final ILaunchConfiguration configuration) {
super.initializeFrom(configuration);
String vmArgs= null;
try {
vmArgs= configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, (String) null);
}
catch (final CoreException e) {
}
this.vmArgsControl.getTextControl().setText(vmArgs != null ? vmArgs : ""); //$NON-NLS-1$
}
@Override
public void performApply(final ILaunchConfigurationWorkingCopy configuration) {
super.performApply(configuration);
final String vmArgs= this.vmArgsControl.getTextControl().getText();
configuration.setAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS,
(vmArgs.length() > 0) ? vmArgs : (String) null);
}
@Override
protected long getUpdateJobDelay() {
return 400;
}
}