/*
* $Id$
*
* SARL is an general-purpose agent programming language.
* More details on http://www.sarl.io
*
* Copyright (C) 2014-2017 the original authors or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.sarl.eclipse.launching.dialog;
import java.lang.ref.SoftReference;
import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat;
import javax.inject.Inject;
import com.google.common.base.Strings;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.internal.ui.SWTFactory;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
import org.eclipse.jdt.internal.debug.ui.actions.ControlAccessibleListener;
import org.eclipse.jdt.internal.debug.ui.launcher.AbstractJavaMainTab;
import org.eclipse.jdt.internal.debug.ui.launcher.DebugTypeSelectionDialog;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Text;
import io.sarl.eclipse.SARLEclipseConfig;
import io.sarl.eclipse.SARLEclipsePlugin;
import io.sarl.eclipse.launching.config.ILaunchConfigurationAccessor;
import io.sarl.eclipse.launching.config.ILaunchConfigurationConfigurator;
import io.sarl.eclipse.launching.config.RootContextIdentifierType;
import io.sarl.eclipse.util.Utilities;
/**
* The main launch configuration tab.
*
* <p>This configuration tab enables to enter the name of the agent to launch,
* the launching parameters, and the SRE.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
public class SARLMainLaunchConfigurationTab extends AbstractJavaMainTab {
private volatile SoftReference<Image> image;
private volatile String lastAgentNameError;
private Text agentNameTextField;
private Button agentNameSearchButton;
private Button showLogoOptionButton;
private Button showLogInfoButton;
private Button offlineButton;
private Button runInEclipseButton;
private Button enableAssertionsInDebugModeButton;
private Button enableAssertionsInRunModeButton;
private Button defaultContextIdentifierButton;
private Button randomContextIdentifierButton;
private Button bootContextIdentifierButton;
private final WidgetListener defaultListener = new WidgetListener();
@Inject
private ILaunchConfigurationConfigurator configurator;
@Inject
private ILaunchConfigurationAccessor accessor;
/** Construct a main configuration tab for SARL project.
*/
public SARLMainLaunchConfigurationTab() {
//
}
@Override
public Image getImage() {
Image img = (this.image == null) ? null : this.image.get();
if (img == null) {
img = SARLEclipsePlugin.getDefault().getImage(SARLEclipseConfig.SARL_LOGO_IMAGE);
this.image = new SoftReference<>(img);
}
return img;
}
@Override
public String getId() {
return "io.sarl.eclipse.debug.ui.sarlMainTab"; //$NON-NLS-1$
}
@Override
public String getName() {
return Messages.MainLaunchConfigurationTab_7;
}
@Override
public void createControl(Composite parent) {
final Composite comp = SWTFactory.createComposite(parent, parent.getFont(), 1, 1, GridData.FILL_BOTH);
((GridLayout) comp.getLayout()).verticalSpacing = 0;
createProjectEditor(comp);
createVerticalSpacer(comp, 1);
createAgentNameEditor(comp, Messages.MainLaunchConfigurationTab_0);
createVerticalSpacer(comp, 1);
createContextIdentifierTypeEditor(comp, Messages.MainLaunchConfigurationTab_9);
createVerticalSpacer(comp, 1);
createLaunchOptionEditor(comp, Messages.MainLaunchConfigurationTab_10);
setControl(comp);
}
/**
* Creates the widgets for specifying a agent name.
*
* @param parent - the parent composite.
* @param text - the label of the group.
*/
protected void createAgentNameEditor(Composite parent, String text) {
final Group group = SWTFactory.createGroup(parent, text, 2, 1, GridData.FILL_HORIZONTAL);
this.agentNameTextField = SWTFactory.createSingleText(group, 1);
this.agentNameTextField.addModifyListener(new ModifyListener() {
@SuppressWarnings("synthetic-access")
@Override
public void modifyText(ModifyEvent event) {
SARLMainLaunchConfigurationTab.this.lastAgentNameError = null;
updateLaunchConfigurationDialog();
}
});
ControlAccessibleListener.addListener(this.agentNameTextField, group.getText());
this.agentNameSearchButton = createPushButton(group, Messages.MainLaunchConfigurationTab_1, null);
this.agentNameSearchButton.addSelectionListener(new SelectionListener() {
@Override
public void widgetDefaultSelected(SelectionEvent event) {
//
}
@Override
public void widgetSelected(SelectionEvent event) {
handleAgentNameSearchButtonSelected();
}
});
}
/**
* Creates the widgets for configuring the context identifier.
*
* @param parent - the parent composite.
* @param text - the label of the group.
*/
protected void createContextIdentifierTypeEditor(Composite parent, String text) {
final Group group = SWTFactory.createGroup(parent, text, 1, 1, GridData.FILL_HORIZONTAL);
this.defaultContextIdentifierButton = createRadioButton(group, Messages.MainLaunchConfigurationTab_11);
this.defaultContextIdentifierButton.addSelectionListener(this.defaultListener);
this.randomContextIdentifierButton = createRadioButton(group, Messages.MainLaunchConfigurationTab_12);
this.randomContextIdentifierButton.addSelectionListener(this.defaultListener);
this.bootContextIdentifierButton = createRadioButton(group, Messages.MainLaunchConfigurationTab_13);
this.bootContextIdentifierButton.addSelectionListener(this.defaultListener);
}
/** Replies the type of context identifier selected by the user.
*
* @return the type of context identifier.
*/
protected RootContextIdentifierType getSelectedContextIdentifierType() {
if (this.randomContextIdentifierButton.getSelection()) {
return RootContextIdentifierType.RANDOM_CONTEXT_ID;
}
if (this.bootContextIdentifierButton.getSelection()) {
return RootContextIdentifierType.BOOT_AGENT_CONTEXT_ID;
}
return RootContextIdentifierType.DEFAULT_CONTEXT_ID;
}
/**
* Creates the widgets for configuring the launch options.
*
* @param parent - the parent composite.
* @param text - the label of the group.
*/
protected void createLaunchOptionEditor(Composite parent, String text) {
final Group group = SWTFactory.createGroup(parent, text, 1, 1, GridData.FILL_HORIZONTAL);
this.showLogoOptionButton = createCheckButton(group, Messages.MainLaunchConfigurationTab_14);
this.showLogoOptionButton.addSelectionListener(this.defaultListener);
this.showLogInfoButton = createCheckButton(group, Messages.MainLaunchConfigurationTab_15);
this.showLogInfoButton.addSelectionListener(this.defaultListener);
this.offlineButton = createCheckButton(group, Messages.MainLaunchConfigurationTab_16);
this.offlineButton.addSelectionListener(this.defaultListener);
this.enableAssertionsInRunModeButton = createCheckButton(group, Messages.SARLMainLaunchConfigurationTab_2);
this.enableAssertionsInRunModeButton.addSelectionListener(this.defaultListener);
this.enableAssertionsInDebugModeButton = createCheckButton(group, Messages.SARLMainLaunchConfigurationTab_1);
this.enableAssertionsInDebugModeButton.addSelectionListener(this.defaultListener);
this.runInEclipseButton = createCheckButton(group, Messages.SARLMainLaunchConfigurationTab_0);
this.runInEclipseButton.addSelectionListener(this.defaultListener);
}
@Override
public void initializeFrom(ILaunchConfiguration config) {
super.initializeFrom(config);
updateAgentNameFromConfig(config);
updateContextIdentifierTypeFromConfig(config);
updateLaunchOptionsFromConfig(config);
}
/**
* Loads the context identifier type from the launch configuration's preference store.
*
* @param config - the config to load the agent name from
*/
protected void updateContextIdentifierTypeFromConfig(ILaunchConfiguration config) {
final RootContextIdentifierType type = this.accessor.getDefaultContextIdentifier(config);
assert type != null;
switch (type) {
case RANDOM_CONTEXT_ID:
this.randomContextIdentifierButton.setSelection(true);
break;
case BOOT_AGENT_CONTEXT_ID:
this.bootContextIdentifierButton.setSelection(true);
break;
case DEFAULT_CONTEXT_ID:
default:
this.defaultContextIdentifierButton.setSelection(true);
break;
}
}
/**
* Loads the launch options from the launch configuration's preference store.
*
* @param config - the config to load the agent name from
*/
protected void updateLaunchOptionsFromConfig(ILaunchConfiguration config) {
final boolean showLogo = this.accessor.getShowLogoFlag(config);
final boolean showLogInfo = this.accessor.getShowLogInfoFlag(config);
final boolean offline = this.accessor.getOfflineFlag(config);
final boolean runInEclipse = this.accessor.isEmbeddedSRE(config);
final boolean enableAssertionsRun = this.accessor.isAssertionEnabledInRunMode(config);
final boolean enableAssertionsDebug = this.accessor.isAssertionEnabledInDebugMode(config);
this.showLogoOptionButton.setSelection(showLogo);
this.showLogInfoButton.setSelection(showLogInfo);
this.offlineButton.setSelection(offline);
this.enableAssertionsInRunModeButton.setSelection(enableAssertionsRun);
this.enableAssertionsInDebugModeButton.setSelection(enableAssertionsDebug);
this.runInEclipseButton.setSelection(runInEclipse);
}
/**
* Loads the agent name from the launch configuration's preference store.
*
* @param config - the config to load the agent name from
*/
protected void updateAgentNameFromConfig(ILaunchConfiguration config) {
final String agentName = this.accessor.getAgent(config);
this.agentNameTextField.setText(Strings.nullToEmpty(agentName));
}
@Override
public boolean isValid(ILaunchConfiguration config) {
setErrorMessage(null);
setMessage(null);
return isValidProjectName() && isValidAgentName()
&& isValidContextIdentifierType() && isValidLaunchOptions();
}
/** Replies if the context identfiier type is valid.
*
* @return the validity state.
*/
@SuppressWarnings("static-method")
protected boolean isValidContextIdentifierType() {
return true;
}
/** Replies if the launch options are valid.
*
* @return the validity state.
*/
@SuppressWarnings("static-method")
protected boolean isValidLaunchOptions() {
return true;
}
/** Replies if the agent name is valid.
*
* @return the validity state.
*/
protected boolean isValidAgentName() {
if (this.lastAgentNameError != null) {
final boolean isValid = Strings.isNullOrEmpty(this.lastAgentNameError);
if (!isValid) {
setErrorMessage(this.lastAgentNameError);
}
return isValid;
}
final String name = this.agentNameTextField.getText();
if (Strings.isNullOrEmpty(name)) {
this.lastAgentNameError = Messages.MainLaunchConfigurationTab_2;
setErrorMessage(this.lastAgentNameError);
return false;
}
if (!isAgentNameDefined(name)) {
this.lastAgentNameError = MessageFormat.format(Messages.MainLaunchConfigurationTab_8, name);
setErrorMessage(this.lastAgentNameError);
return false;
}
this.lastAgentNameError = Utilities.EMPTY_STRING;
return true;
}
/** Replies if the project name is valid.
*
* <p>Copied from JDT.
*
* @return the validity state.
*/
protected boolean isValidProjectName() {
final String name = this.fProjText.getText();
if (Strings.isNullOrEmpty(name)) {
setErrorMessage(Messages.MainLaunchConfigurationTab_3);
return false;
}
final IWorkspace workspace = ResourcesPlugin.getWorkspace();
final IStatus status = workspace.validateName(name, IResource.PROJECT);
if (status.isOK()) {
final IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(name);
if (!project.exists()) {
setErrorMessage(MessageFormat.format(
Messages.MainLaunchConfigurationTab_4, name));
return false;
}
if (!project.isOpen()) {
setErrorMessage(MessageFormat.format(
Messages.MainLaunchConfigurationTab_5, name));
return false;
}
} else {
setErrorMessage(MessageFormat.format(
Messages.MainLaunchConfigurationTab_6, status.getMessage()));
return false;
}
return true;
}
@Override
public void performApply(ILaunchConfigurationWorkingCopy config) {
this.configurator.setProjectName(config, Strings.emptyToNull(this.fProjText.getText().trim()));
this.configurator.setAgent(config, Strings.emptyToNull(this.agentNameTextField.getText().trim()));
this.configurator.setDefaultContextIdentifier(config, getSelectedContextIdentifierType());
this.configurator.setLaunchingFlags(config,
this.showLogoOptionButton.getSelection(),
this.showLogInfoButton.getSelection(),
this.offlineButton.getSelection());
this.configurator.setAssertionEnabledInRunMode(config, this.enableAssertionsInRunModeButton.getSelection());
this.configurator.setAssertionEnabledInDebugMode(config, this.enableAssertionsInDebugModeButton.getSelection());
this.configurator.setEmbeddedSRE(config, this.runInEclipseButton.getSelection());
mapResources(config);
}
@Override
public void setDefaults(ILaunchConfigurationWorkingCopy config) {
final IJavaElement javaElement = getContext();
if (javaElement != null) {
initializeJavaProject(javaElement, config);
} else {
config.removeAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME);
}
initializeAgentName(javaElement, config);
initializeContextIdentifierType(config);
initializeLaunchOptions(config);
}
/**
* Reset the given configuration with the context identifier type.
*
* @param config - the config to set with the context identifier type.
*/
protected void initializeContextIdentifierType(ILaunchConfigurationWorkingCopy config) {
this.configurator.setDefaultContextIdentifier(config, null);
}
/**
* Reset the given configuration with the launch options.
*
* @param config - the config to set with the launch options.
*/
protected void initializeLaunchOptions(ILaunchConfigurationWorkingCopy config) {
this.configurator.setLaunchingFlags(config, null, null, null);
}
private String extractNameFromJavaElement(final IJavaElement javaElement) {
String name = null;
if (javaElement != null) {
final String[] nameRef = new String[1];
try {
getLaunchConfigurationDialog().run(true, true, new IRunnableWithProgress() {
@SuppressWarnings("synthetic-access")
@Override
public void run(IProgressMonitor pm) throws InvocationTargetException {
try {
final IJavaProject javaProject = javaElement.getJavaProject();
final IType agentType = javaProject.findType("io.sarl.lang.core.Agent"); //$NON-NLS-1$
final IType[] types = agentType.newTypeHierarchy(pm).getAllSubtypes(agentType);
if (types != null && types.length > 0) {
nameRef[0] = types[0].getFullyQualifiedName();
}
} catch (JavaModelException e) {
setErrorMessage(e.getLocalizedMessage());
JDIDebugUIPlugin.log(e);
}
}
});
} catch (Exception e) {
setErrorMessage(e.getLocalizedMessage());
JDIDebugUIPlugin.log(e);
}
name = nameRef[0];
}
return Strings.nullToEmpty(name);
}
/**
* Reset the given configuration with the agent name attributes associated
* to the given element.
*
* @param javaElement - the element from which information may be retrieved.
* @param config - the config to set with the agent name.
*/
protected void initializeAgentName(IJavaElement javaElement, ILaunchConfigurationWorkingCopy config) {
String name = extractNameFromJavaElement(javaElement);
// Set the attribute
this.configurator.setAgent(config, name);
// Rename the launch configuration
if (name.length() > 0) {
final int index = name.lastIndexOf('.');
if (index > 0) {
name = name.substring(index + 1);
}
name = getLaunchConfigurationDialog().generateName(name);
config.rename(name);
}
}
private IType[] searchAgentNames() {
final IType[][] res = new IType[1][];
res[0] = new IType[0];
final String projectName = this.fProjText.getText();
final IStatus status = ResourcesPlugin.getWorkspace().validateName(projectName, IResource.PROJECT);
if (status.isOK()) {
try {
getLaunchConfigurationDialog().run(true, true, new IRunnableWithProgress() {
@SuppressWarnings("synthetic-access")
@Override
public void run(IProgressMonitor pm) throws InvocationTargetException {
try {
final IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
final IJavaProject javaProject = JavaCore.create(project);
final IType agentType = javaProject.findType("io.sarl.lang.core.Agent"); //$NON-NLS-1$
res[0] = agentType.newTypeHierarchy(pm).getAllSubtypes(agentType);
} catch (JavaModelException e) {
setErrorMessage(e.getLocalizedMessage());
JDIDebugUIPlugin.log(e);
}
}
});
} catch (Exception e) {
setErrorMessage(e.getLocalizedMessage());
JDIDebugUIPlugin.log(e);
}
}
return res[0];
}
private boolean isAgentNameDefined(final String agentName) {
final String projectName = this.fProjText.getText();
final IStatus status = ResourcesPlugin.getWorkspace().validateName(projectName, IResource.PROJECT);
if (status.isOK()) {
try {
final boolean[] res = new boolean[1];
getLaunchConfigurationDialog().run(true, true, new IRunnableWithProgress() {
@SuppressWarnings("synthetic-access")
@Override
public void run(IProgressMonitor pm) throws InvocationTargetException {
try {
final IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
final IJavaProject javaProject = JavaCore.create(project);
final IType agentType = javaProject.findType("io.sarl.lang.core.Agent"); //$NON-NLS-1$
if (agentType != null) {
final IType type = javaProject.findType(agentName);
if (type != null) {
res[0] = type.newSupertypeHierarchy(pm).contains(agentType);
}
}
} catch (JavaModelException e) {
setErrorMessage(e.getLocalizedMessage());
JDIDebugUIPlugin.log(e);
}
}
});
return res[0];
} catch (Exception exception) {
//
}
}
return false;
}
/** Invoked when the search button for the agent agent was clocked.
*/
protected void handleAgentNameSearchButtonSelected() {
final IType[] types = searchAgentNames();
// Ask to the user
final DebugTypeSelectionDialog mmsd = new DebugTypeSelectionDialog(getShell(),
types, ""); //$NON-NLS-1$
if (mmsd.open() == Window.CANCEL) {
return;
}
final IType type = (IType) mmsd.getFirstResult();
if (type != null) {
this.agentNameTextField.setText(type.getFullyQualifiedName());
this.fProjText.setText(type.getJavaProject().getElementName());
}
}
/** Listener of events in internal components for refreshing the tab.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private class WidgetListener implements SelectionListener {
WidgetListener() {
//
}
@Override
public void widgetDefaultSelected(SelectionEvent event) {
//
}
@SuppressWarnings("synthetic-access")
@Override
public void widgetSelected(SelectionEvent event) {
updateLaunchConfigurationDialog();
}
}
}