/*
* $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.runtime;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import com.google.common.base.MoreObjects;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.debug.internal.ui.SWTFactory;
import org.eclipse.jdt.internal.debug.ui.actions.ControlAccessibleListener;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
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.ui.dialogs.PreferencesUtil;
import io.sarl.eclipse.SARLEclipsePlugin;
import io.sarl.eclipse.preferences.SREsPreferencePage;
/**
* Block of widgets that permits to select and
* configure a SARL runtime environment.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
public class SREConfigurationBlock {
/** Property name for the SRE configuration.
*/
public static final String PROPERTY_SRE_CONFIGURATION = "PROPERTY_SRE_CONFIGURATION"; //$NON-NLS-1$
/**
* This block's control.
*/
private Composite control;
/**
* SRE change listeners.
*/
private final ListenerList<IPropertyChangeListener> listeners = new ListenerList<>();
private final String title;
private ISREInstallChangedListener sreListener;
private final boolean enableSystemWideSelector;
private final List<ProjectSREProviderFactory> projectProviderFactories;
private final ProjectProvider project;
private ProjectSREProvider projectProvider;
private Button systemSREButton;
private Button projectSREButton;
private Button specificSREButton;
private final List<ISREInstall> runtimeEnvironments = new ArrayList<>();
private Combo runtimeEnvironmentCombo;
private Button runtimeEnvironmentSearchButton;
private boolean notify = true;
/**
* @param enableSystemWideSelector - indicates if the system-wide configuration selector must be enabled.
* @param project - the provider of the project that may be associated to the block. If <code>null</code> the components
* related to the project are hidden.
* @param projectProviderFactories - the factories of the provider of a project that may give SRE configuration.
* If <code>null</code> the components related to the project are hidden.
*/
public SREConfigurationBlock(boolean enableSystemWideSelector,
ProjectProvider project,
List<ProjectSREProviderFactory> projectProviderFactories) {
this(null, enableSystemWideSelector, project, projectProviderFactories);
}
/**
* @param title - the title of the group.
* @param enableSystemWideSelector - indicates if the system-wide configuration selector must be enabled.
* @param project - the provider of the project that may be associated to the block. If <code>null</code> the components
* related to the project are hidden.
* @param projectProviderFactories - the factories of the provider of a project that may give SRE configuration.
* If <code>null</code> the components related to the project are hidden.
*/
public SREConfigurationBlock(String title, boolean enableSystemWideSelector,
ProjectProvider project,
List<ProjectSREProviderFactory> projectProviderFactories) {
this.title = title;
this.enableSystemWideSelector = enableSystemWideSelector;
this.project = project;
this.projectProviderFactories = projectProviderFactories != null
? projectProviderFactories : Collections.<ProjectSREProviderFactory>emptyList();
}
/** Change the event notification flag.
*
* @param notify - <code>true</code> for notifying the events.
*/
public void setNotify(boolean notify) {
this.notify = notify;
}
/** Replies if the events are notified.
*
* @return <code>true</code> if the events are notified.
*/
public boolean getNotify() {
return this.notify;
}
/** Add listener on the changes in the SRE configuration.
*
* @param listener - the listener.
*/
public void addPropertyChangeListener(IPropertyChangeListener listener) {
this.listeners.add(listener);
}
/** Add listener on the changes in the SRE configuration.
*
* @param listener - the listener.
*/
public void removePropertyChangeListener(IPropertyChangeListener listener) {
this.listeners.remove(listener);
}
private void firePropertyChange() {
if (this.notify) {
final PropertyChangeEvent event = new PropertyChangeEvent(this,
PROPERTY_SRE_CONFIGURATION, null,
getSelectedSRE());
final Object[] listeners = this.listeners.getListeners();
for (int i = 0; i < listeners.length; i++) {
final IPropertyChangeListener listener = (IPropertyChangeListener) listeners[i];
listener.propertyChange(event);
}
}
}
private ISREInstall retreiveProjectSRE() {
if (this.projectProviderFactories.isEmpty() || this.project == null) {
return null;
}
if (this.projectProvider == null) {
final IProject project = this.project.getProject();
if (project == null) {
return null;
}
final Iterator<ProjectSREProviderFactory> iterator = this.projectProviderFactories.iterator();
while (this.projectProvider == null && iterator.hasNext()) {
final ProjectSREProviderFactory factory = iterator.next();
this.projectProvider = factory.getProjectSREProvider(project);
}
if (this.projectProvider == null) {
return null;
}
}
return this.projectProvider.getProjectSREInstall();
}
private void doSystemSREButtonClick() {
if (this.specificSREButton != null) {
this.specificSREButton.setSelection(false);
}
if (this.projectSREButton != null) {
this.projectSREButton.setSelection(false);
}
if (this.systemSREButton != null) {
this.systemSREButton.setSelection(true);
}
handleSystemWideConfigurationSelected();
}
private void doProjectSREButtonClick() {
if (this.specificSREButton != null) {
this.specificSREButton.setSelection(false);
}
if (this.systemSREButton != null) {
this.systemSREButton.setSelection(false);
}
if (this.projectSREButton != null) {
this.projectSREButton.setSelection(true);
}
handleProjectConfigurationSelected();
}
private void doSpecificSREButtonClick() {
if (this.systemSREButton != null) {
this.systemSREButton.setSelection(false);
}
if (this.projectSREButton != null) {
this.projectSREButton.setSelection(false);
}
if (this.specificSREButton != null) {
this.specificSREButton.setSelection(true);
}
handleSpecificConfigurationSelected();
}
private void createSystemWideSelector(Group parent) {
if (this.enableSystemWideSelector) {
final ISREInstall wideSystemSRE = SARLRuntime.getDefaultSREInstall();
final String wideSystemSRELabel;
if (wideSystemSRE == null) {
wideSystemSRELabel = Messages.SREConfigurationBlock_0;
} else {
wideSystemSRELabel = wideSystemSRE.getName();
}
this.systemSREButton = SWTFactory.createRadioButton(parent,
MessageFormat.format(
Messages.SREConfigurationBlock_1, wideSystemSRELabel), 3);
this.systemSREButton.addSelectionListener(new SelectionAdapter() {
@SuppressWarnings("synthetic-access")
@Override
public void widgetSelected(SelectionEvent event) {
if (SREConfigurationBlock.this.systemSREButton.getSelection()) {
handleSystemWideConfigurationSelected();
}
}
});
}
}
private void createSRESelector(Group parent) {
this.runtimeEnvironmentCombo = SWTFactory.createCombo(
parent,
SWT.DROP_DOWN | SWT.READ_ONLY,
1,
new String[0]);
this.runtimeEnvironmentCombo.addSelectionListener(new SelectionAdapter() {
@SuppressWarnings("synthetic-access")
@Override
public void widgetSelected(SelectionEvent event) {
firePropertyChange();
}
});
ControlAccessibleListener.addListener(this.runtimeEnvironmentCombo, parent.getText());
this.runtimeEnvironmentSearchButton = SWTFactory.createPushButton(
parent, Messages.SREConfigurationBlock_4, null);
this.runtimeEnvironmentSearchButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
handleInstalledSREsButtonSelected();
}
});
}
private void createProjectSelector(Group parent) {
if (!this.projectProviderFactories.isEmpty()) {
this.projectSREButton = SWTFactory.createRadioButton(parent,
MessageFormat.format(
Messages.SREConfigurationBlock_3, Messages.SREConfigurationBlock_0), 3);
this.projectSREButton.addSelectionListener(new SelectionAdapter() {
@SuppressWarnings("synthetic-access")
@Override
public void widgetSelected(SelectionEvent event) {
if (SREConfigurationBlock.this.projectSREButton.getSelection()) {
handleProjectConfigurationSelected();
}
}
});
}
}
/**
* Creates this block's control in the given control.
*
* @param parent - containing control
* @return the control.
* @see #getControl()
*/
public Control createControl(Composite parent) {
this.control = SWTFactory.createComposite(
parent, parent.getFont(), 1, 1, GridData.FILL_HORIZONTAL);
final int nbColumns = this.enableSystemWideSelector ? 3 : 2;
final Group group = SWTFactory.createGroup(this.control,
MoreObjects.firstNonNull(this.title, Messages.SREConfigurationBlock_7),
nbColumns, 1, GridData.FILL_HORIZONTAL);
if (this.enableSystemWideSelector || !this.projectProviderFactories.isEmpty()) {
createSystemWideSelector(group);
createProjectSelector(group);
this.specificSREButton = SWTFactory.createRadioButton(group,
Messages.SREConfigurationBlock_2, 1);
this.specificSREButton.addSelectionListener(new SelectionAdapter() {
@SuppressWarnings("synthetic-access")
@Override
public void widgetSelected(SelectionEvent event) {
if (SREConfigurationBlock.this.specificSREButton.getSelection()) {
handleSpecificConfigurationSelected();
}
}
});
} else {
this.systemSREButton = null;
this.projectSREButton = null;
this.specificSREButton = null;
}
createSRESelector(group);
return getControl();
}
/** Update the label of the "system-wide" and "project" configuration buttons.
*/
public void updateExternalSREButtonLabels() {
if (this.enableSystemWideSelector) {
final ISREInstall wideSystemSRE = SARLRuntime.getDefaultSREInstall();
final String wideSystemSRELabel;
if (wideSystemSRE == null) {
wideSystemSRELabel = Messages.SREConfigurationBlock_0;
} else {
wideSystemSRELabel = wideSystemSRE.getName();
}
this.systemSREButton.setText(MessageFormat.format(
Messages.SREConfigurationBlock_1, wideSystemSRELabel));
}
if (!this.projectProviderFactories.isEmpty()) {
final ISREInstall projectSRE = retreiveProjectSRE();
final String projectSRELabel;
if (projectSRE == null) {
projectSRELabel = Messages.SREConfigurationBlock_0;
} else {
projectSRELabel = projectSRE.getName();
}
this.projectSREButton.setText(MessageFormat.format(
Messages.SREConfigurationBlock_3, projectSRELabel));
}
}
/**
* Returns this block's control.
*
* @return control
*/
public Composite getControl() {
return this.control;
}
/** Change the selected SRE. If <code>null</code> is given as parameter,
* and this block enables to select the system-wide SRE, then
* the system-wide SRE is selected. If the block is not enabling the
* system-wide SRE, the specific SRE is reset to the system-wide
* SRE.
*
* @param sre - the sre, if <code>null</code> reset to default.
* @return <code>true</code> if the selection changed.
* @see #selectSpecificSRE(ISREInstall)
*/
public final boolean selectSRE(ISREInstall sre) {
if (sre == null) {
if (this.enableSystemWideSelector) {
return selectSystemWideSRE();
}
if (!this.projectProviderFactories.isEmpty()) {
return selectProjectSRE();
}
}
return selectSpecificSRE(sre);
}
/** Select the system-wide SRE.
*
* @return <code>true</code> if the selection changed.
*/
public boolean selectSystemWideSRE() {
if (this.enableSystemWideSelector && !this.systemSREButton.getSelection()) {
doSystemSREButtonClick();
return true;
}
return false;
}
/** Select the project SRE.
*
* @return <code>true</code> if the selection changed.
*/
public boolean selectProjectSRE() {
if (!this.projectProviderFactories.isEmpty() && !this.projectSREButton.getSelection()) {
doProjectSREButtonClick();
return true;
}
return false;
}
/** Select the specific SRE.
*
* @return <code>true</code> if the selection changed.
*/
public boolean selectSpecificSRE() {
if (this.enableSystemWideSelector && this.systemSREButton.getSelection()) {
doSpecificSREButtonClick();
return true;
}
return false;
}
/** Select a specific SRE.
*
* @param sre - the sre, if <code>null</code> reset to default.
* @return <code>true</code> if the selection changed.
*/
@SuppressWarnings("checkstyle:npathcomplexity")
public boolean selectSpecificSRE(ISREInstall sre) {
ISREInstall theSRE = sre;
if (theSRE == null) {
theSRE = SARLRuntime.getDefaultSREInstall();
}
if (theSRE != null) {
boolean changed = false;
final boolean oldNotify = this.notify;
try {
this.notify = false;
if (isSystemWideDefaultSRE()) {
doSpecificSREButtonClick();
changed = true;
}
final ISREInstall oldSRE = getSelectedSRE();
if (oldSRE != theSRE) {
final int index = indexOf(theSRE);
if (index >= 0) {
this.runtimeEnvironmentCombo.select(index);
changed = true;
}
}
if (!this.runtimeEnvironments.isEmpty()) {
int selection = this.runtimeEnvironmentCombo.getSelectionIndex();
if (selection < 0 || selection >= this.runtimeEnvironments.size()) {
// Ensure that there is a selected element
selection = indexOf(theSRE);
this.runtimeEnvironmentCombo.select((selection < 0) ? 0 : selection);
changed = true;
}
}
} finally {
this.notify = oldNotify;
}
if (changed) {
firePropertyChange();
return true;
}
}
return false;
}
private int indexOf(ISREInstall sre) {
if (sre != null) {
final Iterator<ISREInstall> iterator = this.runtimeEnvironments.iterator();
for (int i = 0; iterator.hasNext(); ++i) {
final ISREInstall knownSRE = iterator.next();
if (knownSRE.getId().equals(sre.getId())) {
return i;
}
}
}
return -1;
}
private String[] getSRELabels() {
final String[] labels = new String[this.runtimeEnvironments.size()];
for (int i = 0; i < this.runtimeEnvironments.size(); ++i) {
labels[i] = this.runtimeEnvironments.get(i).getName();
}
return labels;
}
/** Replies if the user has selected the default system-wide SRE.
*
* @return <code>true</code> if the user has selected the default
* system-wide SRE.
* @see #getSelectedSRE()
*/
public boolean isSystemWideDefaultSRE() {
return this.enableSystemWideSelector && this.systemSREButton.getSelection();
}
/** Replies if the user has selected the project SRE.
*
* @return <code>true</code> if the user has selected the project SRE.
* @see #getSelectedSRE()
*/
public boolean isProjectSRE() {
return !this.projectProviderFactories.isEmpty() && this.projectSREButton.getSelection();
}
/** Replies the selected SARL runtime environment.
*
* @return the SARL runtime environment or <code>null</code> if
* there is no selected SRE.
* @see #isSystemWideDefaultSRE()
*/
public ISREInstall getSelectedSRE() {
if (this.enableSystemWideSelector && this.systemSREButton.getSelection()) {
return SARLRuntime.getDefaultSREInstall();
}
if (!this.projectProviderFactories.isEmpty() && this.projectSREButton.getSelection()) {
return retreiveProjectSRE();
}
return getSpecificSRE();
}
/** Replies the specific SARL runtime environment.
*
* @return the SARL runtime environment or <code>null</code> if
* there is no selected SRE.
* @see #isSystemWideDefaultSRE()
*/
public ISREInstall getSpecificSRE() {
final int index = this.runtimeEnvironmentCombo.getSelectionIndex();
if (index >= 0 && index < this.runtimeEnvironments.size()) {
return this.runtimeEnvironments.get(index);
}
return null;
}
/** Update the enable state of the block.
*/
public void updateEnableState() {
boolean comboEnabled = !this.runtimeEnvironments.isEmpty();
boolean searchEnabled = true;
if (isSystemWideDefaultSRE() || isProjectSRE()) {
comboEnabled = false;
searchEnabled = false;
}
this.runtimeEnvironmentCombo.setEnabled(comboEnabled);
this.runtimeEnvironmentSearchButton.setEnabled(searchEnabled);
}
/** Initialize the block with the installed JREs.
* The selection and the components states are not updated
* by this function.
*/
public void initialize() {
// Initialize the SRE list
this.runtimeEnvironments.clear();
final ISREInstall[] sres = SARLRuntime.getSREInstalls();
Arrays.sort(sres, new Comparator<ISREInstall>() {
@Override
public int compare(ISREInstall o1, ISREInstall o2) {
return o1.getName().compareTo(o2.getName());
}
});
final List<String> labels = new ArrayList<>(sres.length);
for (int i = 0; i < sres.length; ++i) {
if (sres[i].getValidity().isOK()) {
this.runtimeEnvironments.add(sres[i]);
labels.add(sres[i].getName());
}
}
this.runtimeEnvironmentCombo.setItems(labels.toArray(new String[labels.size()]));
// Reset the labels of the external SRE configurations
updateExternalSREButtonLabels();
// Initialize the type of configuration
if (this.enableSystemWideSelector) {
this.specificSREButton.setSelection(false);
if (!this.projectProviderFactories.isEmpty()) {
this.projectSREButton.setSelection(false);
}
this.systemSREButton.setSelection(true);
} else if (!this.projectProviderFactories.isEmpty()) {
this.specificSREButton.setSelection(false);
if (this.enableSystemWideSelector) {
this.systemSREButton.setSelection(false);
}
this.projectSREButton.setSelection(true);
}
// Wait for SRE list updates.
this.sreListener = new InstallChange();
SARLRuntime.addSREInstallChangedListener(this.sreListener);
updateEnableState();
}
/** Dispose the block.
*/
public void dispose() {
if (this.sreListener != null) {
SARLRuntime.removeSREInstallChangedListener(this.sreListener);
this.sreListener = null;
}
}
/** Invoked when the user want to search for a SARL runtime environment.
*/
protected void handleInstalledSREsButtonSelected() {
PreferencesUtil.createPreferenceDialogOn(
getControl().getShell(),
SREsPreferencePage.ID,
new String[] {SREsPreferencePage.ID},
null).open();
}
/** Invoked when the user select the "system-wide" configuration.
*/
protected void handleSystemWideConfigurationSelected() {
updateEnableState();
firePropertyChange();
}
/** Invoked when the user select the "project" configuration.
*/
protected void handleProjectConfigurationSelected() {
updateEnableState();
firePropertyChange();
}
/** Invoked when the user select the "specific" configuration.
*/
protected void handleSpecificConfigurationSelected() {
updateEnableState();
firePropertyChange();
}
/** Validate that the given SRE is valid in the context of the SRE configuration.
*
* @param sre - the SRE.
* @return the state of the validation, never <code>null</code>.
*/
public IStatus validate(ISREInstall sre) {
final IStatus status;
if (this.enableSystemWideSelector && this.systemSREButton.getSelection()) {
if (SARLRuntime.getDefaultSREInstall() == null) {
status = SARLEclipsePlugin.getDefault().createStatus(IStatus.ERROR, Messages.SREConfigurationBlock_5);
} else {
status = SARLEclipsePlugin.getDefault().createOkStatus();
}
} else if (!this.projectProviderFactories.isEmpty() && this.projectSREButton.getSelection()) {
if (retreiveProjectSRE() == null) {
status = SARLEclipsePlugin.getDefault().createStatus(IStatus.ERROR,
Messages.SREConfigurationBlock_6);
} else {
status = SARLEclipsePlugin.getDefault().createOkStatus();
}
} else if (this.runtimeEnvironments.isEmpty()) {
status = SARLEclipsePlugin.getDefault().createStatus(IStatus.ERROR,
Messages.SREConfigurationBlock_8);
} else {
status = sre.getValidity();
}
return status;
}
/** Listener of changes in the configuration block for SREs.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private class InstallChange implements ISREInstallChangedListener {
InstallChange() {
//
}
@SuppressWarnings("synthetic-access")
@Override
public void sreRemoved(ISREInstall sre) {
int index = indexOf(sre);
if (index >= 0) {
SREConfigurationBlock.this.runtimeEnvironments.remove(index);
SREConfigurationBlock.this.runtimeEnvironmentCombo.setItems(getSRELabels());
updateEnableState();
index = SREConfigurationBlock.this.runtimeEnvironmentCombo.getSelectionIndex();
if (index < 0) {
index = SREConfigurationBlock.this.runtimeEnvironmentCombo.getItemCount() - 1;
SREConfigurationBlock.this.runtimeEnvironmentCombo.select(index);
}
}
}
@SuppressWarnings("synthetic-access")
@Override
public void sreChanged(org.eclipse.jdt.launching.PropertyChangeEvent event) {
if (!PROPERTY_NAME.equals(event.getProperty())) {
return;
}
final ISREInstall sre = (ISREInstall) event.getSource();
if (indexOf(sre) >= 0) {
SREConfigurationBlock.this.runtimeEnvironmentCombo.setItems(getSRELabels());
// Update the selection
int index = SREConfigurationBlock.this.runtimeEnvironmentCombo.getSelectionIndex();
if (index < 0) {
index = indexOf(sre);
if (index < 0) {
index = SREConfigurationBlock.this.runtimeEnvironmentCombo.getItemCount() - 1;
}
SREConfigurationBlock.this.runtimeEnvironmentCombo.select(index);
}
// Update the enabling state
if (sre == SARLRuntime.getDefaultSREInstall()) {
updateExternalSREButtonLabels();
}
}
}
@SuppressWarnings({"synthetic-access", "checkstyle:variabledeclarationusagedistance"})
@Override
public void sreAdded(ISREInstall sre) {
if (sre.getValidity().isOK()) {
final ISREInstall current = getSpecificSRE();
SREConfigurationBlock.this.runtimeEnvironments.add(sre);
SREConfigurationBlock.this.runtimeEnvironmentCombo.setItems(getSRELabels());
updateEnableState();
int index = indexOf(current);
if (index < 0
&& SREConfigurationBlock.this.runtimeEnvironmentCombo.getItemCount() > 0) {
index = 0;
}
if (index >= 0) {
SREConfigurationBlock.this.runtimeEnvironmentCombo.select(index);
}
}
}
@Override
public void defaultSREInstallChanged(ISREInstall previous, ISREInstall current) {
updateExternalSREButtonLabels();
}
}
}