blob: 66f4b5419a0e0ba244cf2475a7a00ca480c639c4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2018 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ui.externaltools.internal.ui;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.externaltools.internal.IExternalToolConstants;
import org.eclipse.core.externaltools.internal.model.BuilderCoreUtils;
import org.eclipse.core.externaltools.internal.model.ExternalToolBuilder;
import org.eclipse.core.resources.ICommand;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceDescription;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationListener;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.window.Window;
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.events.SelectionListener;
import org.eclipse.swt.graphics.Font;
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.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.ElementListSelectionDialog;
import org.eclipse.ui.dialogs.PropertyPage;
import org.eclipse.ui.externaltools.internal.launchConfigurations.ExternalToolsMainTab;
import org.eclipse.ui.externaltools.internal.launchConfigurations.ExternalToolsUtil;
import org.eclipse.ui.externaltools.internal.launchConfigurations.IgnoreWhiteSpaceComparator;
import org.eclipse.ui.externaltools.internal.model.BuilderUtils;
import org.eclipse.ui.externaltools.internal.model.ExternalToolsPlugin;
import org.eclipse.ui.externaltools.internal.model.IExternalToolsHelpContextIds;
import org.eclipse.ui.externaltools.internal.model.IPreferenceConstants;
import org.eclipse.ui.progress.IProgressService;
/**
* Property page to add external tools builders.
*/
public final class BuilderPropertyPage extends PropertyPage implements ICheckStateListener {
//locally mark a command's enabled state so it can be processed correctly on performOK
private static final String COMMAND_ENABLED= "CommandEnabled"; //$NON-NLS-1$
private Button upButton, downButton, newButton, importButton, editButton, removeButton;
private boolean userHasMadeChanges= false;
private List<ILaunchConfiguration> configsToBeDeleted = null;
private List<ICommand> commandsToBeDeleted = null;
private CheckboxTableViewer viewer= null;
private boolean fWarned = false;
/**
* Flag to know if we can perform an edit of the selected element(s)
*/
private boolean fCanEdit = false;
private ILabelProvider labelProvider= new BuilderLabelProvider();
/**
* Error configs are objects representing entries pointing to
* invalid launch configurations
*/
public static class ErrorConfig {
private ICommand command;
public ErrorConfig(ICommand command) {
this.command= command;
}
public ICommand getCommand() {
return command;
}
}
/**
* Collection of configurations created while the page is open.
* Stored here so they can be deleted if the page is cancelled.
*/
private List<ILaunchConfiguration> newConfigList = new ArrayList<>();
private SelectionListener buttonListener= new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
handleButtonPressed((Button) e.widget);
}
};
/**
* Launch configuration listener which is responsible for updating items in
* the tree when the user renames configurations in the dialog.
*
* This is necessary because when we tell the configuration dialog to open
* on a launch config and the user renames that config, the old config (the
* one in the tree) is made obsolete and a new config is created. This
* listener hears when new configurations are created this way and replaces
* the old configuration with the new.
*/
private ILaunchConfigurationListener configurationListener= new ILaunchConfigurationListener() {
/**
* A launch configuration has been added. If this config has been
* movedFrom a configuration in the tree, replace the old config with
* the new.
*/
@Override
public void launchConfigurationAdded(final ILaunchConfiguration configuration) {
ILaunchManager manager= DebugPlugin.getDefault().getLaunchManager();
final ILaunchConfiguration oldConfig= manager.getMovedFrom(configuration);
if (oldConfig == null) {
return;
}
//Replace the movedFrom config in the list of newly created configs
if (newConfigList.remove(oldConfig)) {
newConfigList.add(configuration);
}
Display.getDefault().asyncExec(() -> {
TableItem[] items = viewer.getTable().getItems();
for (TableItem item : items) {
Object data = item.getData();
if (data == oldConfig) {
// Found the movedFrom config in the tree. Replace it
// with the new config
item.setData(configuration);
viewer.update(configuration, null);
break;
}
}
});
}
@Override
public void launchConfigurationChanged(ILaunchConfiguration configuration) {
}
@Override
public void launchConfigurationRemoved(ILaunchConfiguration configuration) {
}
};
/**
* Creates an initialized property page
*/
public BuilderPropertyPage() {
super();
noDefaultAndApplyButton();
}
/**
* Add the project's build to the table viewer.
*/
private void addBuildersToTable() {
IProject project = getInputProject();
if (project == null) {
return;
}
//add build spec entries to the table
ICommand[] commands= null;
try {
commands = project.getDescription().getBuildSpec();
} catch (CoreException e) {
handleException(e);
return;
}
boolean projectNeedsMigration= false;
for (ICommand command : commands) {
String[] version= new String[] {IExternalToolConstants.EMPTY_STRING};
ILaunchConfiguration config = BuilderUtils.configFromBuildCommandArgs(project, command.getArguments(), version);
if (BuilderCoreUtils.VERSION_2_1.equals(version[0])) {
// Storing the .project file of a project with 2.1 configs, will
// edit the file in a way that isn't backwards compatible.
projectNeedsMigration= true;
}
Object element= null;
if (config != null) {
if (!config.isWorkingCopy() && !config.exists()) {
Shell shell= getShell();
if (shell == null) {
return;
}
IStatus status = new Status(IStatus.ERROR, ExternalToolsPlugin.PLUGIN_ID, 0, NLS.bind(ExternalToolsUIMessages.BuilderPropertyPage_Exists, new String[]{config.getName()}), null);
ErrorDialog.openError(getShell(), ExternalToolsUIMessages.BuilderPropertyPage_errorTitle,
NLS.bind(ExternalToolsUIMessages.BuilderPropertyPage_External_Tool_Builder__0__Not_Added_2, new String[]{config.getName()}),
status);
userHasMadeChanges= true;
} else {
element= config;
}
} else {
String builderID = command.getBuilderName();
if (builderID.equals(ExternalToolBuilder.ID) && command.getArguments().get(BuilderCoreUtils.LAUNCH_CONFIG_HANDLE) != null) {
// An invalid external tool entry.
element = new ErrorConfig(command);
} else {
element = command;
}
}
if (element != null) {
viewer.add(element);
viewer.setChecked(element, isEnabled(element));
}
}
if (projectNeedsMigration) {
IPreferenceStore store= ExternalToolsPlugin.getDefault().getPreferenceStore();
boolean prompt= store.getBoolean(IPreferenceConstants.PROMPT_FOR_PROJECT_MIGRATION);
boolean proceed= true;
if (prompt) {
Shell shell= getShell();
if (shell == null) {
return;
}
MessageDialogWithToggle dialog= MessageDialogWithToggle.openYesNoQuestion(shell, ExternalToolsUIMessages.BuilderPropertyPage_0, ExternalToolsUIMessages.BuilderPropertyPage_1, ExternalToolsUIMessages.BuilderPropertyPage_2, false, null, null);
proceed= dialog.getReturnCode() == IDialogConstants.YES_ID;
store.setValue(IPreferenceConstants.PROMPT_FOR_PROJECT_MIGRATION, !dialog.getToggleState());
}
if (!proceed) {
// Open the page read-only
viewer.getTable().setEnabled(false);
downButton.setEnabled(false);
editButton.setEnabled(false);
importButton.setEnabled(false);
newButton.setEnabled(false);
removeButton.setEnabled(false);
}
}
}
/**
* Creates and returns a button with the given label, id, and enablement.
*/
private Button createButton(Composite parent, String label) {
Button button = new Button(parent, SWT.PUSH);
button.setFont(parent.getFont());
button.setText(label);
button.setEnabled(false);
button.addSelectionListener(buttonListener);
GridData data = new GridData(GridData.FILL_HORIZONTAL);
data.grabExcessHorizontalSpace = true;
button.setLayoutData(data);
int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
data.widthHint = Math.max(widthHint, button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x);
return button;
}
@Override
protected Control createContents(Composite parent) {
PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, IExternalToolsHelpContextIds.EXTERNAL_TOOLS_BUILDER_PROPERTY_PAGE);
Font font = parent.getFont();
Composite topLevel = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout();
layout.marginHeight = 0;
layout.marginWidth = 0;
topLevel.setLayout(layout);
topLevel.setLayoutData(new GridData(GridData.FILL_BOTH));
Label description = new Label(topLevel, SWT.WRAP);
description.setText(ExternalToolsUIMessages.BuilderPropertyPage_description);
description.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
description.setFont(font);
Composite tableAndButtons = new Composite(topLevel, SWT.NONE);
tableAndButtons.setLayoutData(new GridData(GridData.FILL_BOTH));
layout = new GridLayout();
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.numColumns = 2;
tableAndButtons.setLayout(layout);
// table of builders and tools
viewer= CheckboxTableViewer.newCheckList(tableAndButtons, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
viewer.setLabelProvider(labelProvider);
viewer.addCheckStateListener(this);
Table builderTable= viewer.getTable();
builderTable.setLayoutData(new GridData(GridData.FILL_BOTH));
builderTable.setFont(font);
builderTable.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
handleTableSelectionChanged();
}
});
builderTable.addListener(SWT.MouseDoubleClick, event -> {
// we must not allow editing of elements that cannot be edited via
// the selection changed logic
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=386820
if (fCanEdit) {
handleEditButtonPressed();
}
});
//button area
Composite buttonArea = new Composite(tableAndButtons, SWT.NONE);
layout = new GridLayout();
layout.marginHeight = 0;
layout.marginWidth = 0;
buttonArea.setLayout(layout);
buttonArea.setFont(font);
buttonArea.setLayoutData(new GridData(GridData.FILL_VERTICAL));
newButton = createButton(buttonArea, ExternalToolsUIMessages.BuilderPropertyPage_newButton);
importButton = createButton(buttonArea, ExternalToolsUIMessages.BuilderPropertyPage__Import____3);
editButton = createButton(buttonArea, ExternalToolsUIMessages.BuilderPropertyPage_editButton);
removeButton = createButton(buttonArea, ExternalToolsUIMessages.BuilderPropertyPage_removeButton);
new Label(buttonArea, SWT.LEFT);
upButton = createButton(buttonArea, ExternalToolsUIMessages.BuilderPropertyPage_upButton);
downButton = createButton(buttonArea, ExternalToolsUIMessages.BuilderPropertyPage_downButton);
newButton.setEnabled(true);
importButton.setEnabled(true);
//populate widget contents
addBuildersToTable();
return topLevel;
}
/**
* Turns auto-building on or off in the workspace.
*/
private void setAutobuild(boolean newState) throws CoreException {
IWorkspace workspace= ResourcesPlugin.getWorkspace();
IWorkspaceDescription wsDescription= workspace.getDescription();
boolean oldState= wsDescription.isAutoBuilding();
if (oldState != newState) {
wsDescription.setAutoBuilding(newState);
workspace.setDescription(wsDescription);
}
}
/**
* Returns the project that is the input for this property page,
* or <code>null</code>.
*/
private IProject getInputProject() {
IAdaptable element = getElement();
if (element instanceof IProject) {
return (IProject) element;
}
Object resource = element.getAdapter(IResource.class);
if (resource instanceof IProject) {
return (IProject) resource;
}
return null;
}
/**
* One of the buttons has been pressed, act accordingly.
*/
private void handleButtonPressed(Button button) {
if (button == newButton) {
handleNewButtonPressed();
} else if (button == importButton) {
handleImportButtonPressed();
} else if (button == editButton) {
handleEditButtonPressed();
} else if (button == removeButton) {
handleRemoveButtonPressed();
} else if (button == upButton) {
moveSelectionUp();
} else if (button == downButton) {
moveSelectionDown();
}
if (getControl().isDisposed()) {
return;
}
handleTableSelectionChanged();
viewer.getTable().setFocus();
}
@Override
public void checkStateChanged(CheckStateChangedEvent event) {
Object element= event.getElement();
boolean checked = event.getChecked();
if (element instanceof ILaunchConfiguration) {
enableLaunchConfiguration((ILaunchConfiguration) element, checked);
} else if (element instanceof ICommand) {
Shell shell= getShell();
if (shell == null) {
return;
}
if (checked) {
enableCommand((ICommand)element, checked);
return;
} else if (!fWarned) {
if(MessageDialog.openConfirm(shell, ExternalToolsUIMessages.BuilderPropertyPage_6, ExternalToolsUIMessages.BuilderPropertyPage_7)) {
fWarned = true;
}
}
if(fWarned) {
enableCommand((ICommand)element, checked);
}
else {
viewer.removeCheckStateListener(this);
viewer.setChecked(element, true);
viewer.addCheckStateListener(this);
}
}
}
private void enableLaunchConfiguration(ILaunchConfiguration configuration, boolean enable) {
ILaunchConfigurationWorkingCopy workingCopy= null;
try {
if (configuration instanceof ILaunchConfigurationWorkingCopy) {
workingCopy = (ILaunchConfigurationWorkingCopy) configuration;
} else {
// Replace the config with a working copy
TableItem[] items= viewer.getTable().getItems();
for (TableItem item : items) {
if (item.getData() == configuration) {
workingCopy = configuration.getWorkingCopy();
item.setData(workingCopy);
}
}
}
if (workingCopy != null) {
workingCopy.setAttribute(IExternalToolConstants.ATTR_BUILDER_ENABLED, enable);
}
} catch (CoreException e) {
return;
}
userHasMadeChanges= true;
}
private void enableCommand(ICommand command, boolean enable) {
Map<String, String> args = command.getArguments();
if (args == null) {
args = new HashMap<>(1);
}
args.put(COMMAND_ENABLED, Boolean.toString(enable));
command.setArguments(args);
userHasMadeChanges= true;
}
/**
* The user has pressed the import button. Prompt them to select a
* configuration to import from the workspace.
*/
private void handleImportButtonPressed() {
ILaunchManager manager= DebugPlugin.getDefault().getLaunchManager();
List<ILaunchConfigurationType> toolTypes = getConfigurationTypes(IExternalToolConstants.ID_EXTERNAL_TOOLS_LAUNCH_CATEGORY);
List<ILaunchConfiguration> configurations = new ArrayList<>();
for (ILaunchConfigurationType type : toolTypes) {
try {
ILaunchConfiguration[] configs = manager.getLaunchConfigurations(type);
for (ILaunchConfiguration launchConfiguration : configs) {
if (!DebugUITools.isPrivate(launchConfiguration)) {
configurations.add(launchConfiguration);
}
}
} catch (CoreException e) {
}
}
Shell shell= getShell();
if (shell == null) {
return;
}
ElementListSelectionDialog dialog= new ElementListSelectionDialog(shell, new BuilderLabelProvider());
dialog.setTitle(ExternalToolsUIMessages.BuilderPropertyPage_4);
dialog.setMessage(ExternalToolsUIMessages.BuilderPropertyPage_5);
dialog.setElements(configurations.toArray());
if (dialog.open() == Window.CANCEL) {
return;
}
Object results[]= dialog.getResult();
if (results.length == 0) { //OK pressed with nothing selected
return;
}
ILaunchConfiguration config= (ILaunchConfiguration) results[0];
ILaunchConfiguration newConfig= null;
boolean wasAutobuilding= ResourcesPlugin.getWorkspace().getDescription().isAutoBuilding();
try {
setAutobuild(false);
newConfig= BuilderUtils.duplicateConfiguration(getInputProject(), config);
} catch (CoreException e) {
handleException(e);
} finally {
try {
setAutobuild(wasAutobuilding);
} catch (CoreException e) {
handleException(e);
}
}
if (newConfig != null) {
userHasMadeChanges= true;
viewer.add(newConfig);
viewer.setChecked(newConfig, isEnabled(newConfig));
newConfigList.add(newConfig);
}
}
/**
* The user has pressed the remove button. Delete the selected builder.
*/
private void handleRemoveButtonPressed() {
IStructuredSelection selection = viewer.getStructuredSelection();
if (selection != null) {
int numSelected= selection.size();
userHasMadeChanges= true;
Iterator<?> iterator = selection.iterator();
while (iterator.hasNext()) {
Object item= iterator.next();
if (item instanceof ILaunchConfiguration) {
if (configsToBeDeleted == null) {
configsToBeDeleted = new ArrayList<>(numSelected);
}
configsToBeDeleted.add((ILaunchConfiguration) item);
} else if (item instanceof ICommand) {
if (commandsToBeDeleted == null) {
commandsToBeDeleted = new ArrayList<>(numSelected);
}
commandsToBeDeleted.add((ICommand) item);
}
viewer.remove(item);
}
}
}
/**
* The user has pressed the new button. Create a new configuration and open
* the launch configuration edit dialog on the new config.
*/
private void handleNewButtonPressed() {
ILaunchConfigurationType type = promptForConfigurationType();
if (type == null) {
return;
}
boolean wasAutobuilding= ResourcesPlugin.getWorkspace().getDescription().isAutoBuilding();
try {
ILaunchConfigurationWorkingCopy workingCopy = null;
String name= DebugPlugin.getDefault().getLaunchManager().generateLaunchConfigurationName(ExternalToolsUIMessages.BuilderPropertyPage_New_Builder_7);
workingCopy = type.newInstance(BuilderUtils.getBuilderFolder(getInputProject(), true), name);
StringBuilder buffer= new StringBuilder(IExternalToolConstants.BUILD_TYPE_FULL);
buffer.append(',');
buffer.append(IExternalToolConstants.BUILD_TYPE_INCREMENTAL);
buffer.append(',');
workingCopy.setAttribute(IExternalToolConstants.ATTR_RUN_BUILD_KINDS, buffer.toString());
workingCopy.setAttribute(ExternalToolsMainTab.FIRST_EDIT, true);
ILaunchConfiguration config = null;
setAutobuild(false);
config = workingCopy.doSave();
//needs to be added here in case the user hits apply in the edit dialog
//then we can correctly update the list with the new config.
newConfigList.add(config);
int code= editConfiguration(config);
if (code == Window.CANCEL) {
// If the user cancelled, delete the newly created config
newConfigList.remove(config);
config.delete();
} else {
userHasMadeChanges= true;
//retrieve the last "new" config
//may have been changed by the user pressing apply in the edit dialog
config= newConfigList.get(newConfigList.size() - 1);
viewer.add(config);
viewer.setChecked(config, isEnabled(config));
}
} catch (CoreException e) {
handleException(e);
} finally {
try {
setAutobuild(wasAutobuilding);
} catch (CoreException e) {
handleException(e);
}
}
}
/**
* Prompts the user to edit the given launch configuration. Returns the
* return code from opening the launch configuration dialog.
*/
private int editConfiguration(ILaunchConfiguration config) {
ILaunchManager manager= DebugPlugin.getDefault().getLaunchManager();
manager.addLaunchConfigurationListener(configurationListener);
Shell shell= getShell();
if (shell == null) {
return Window.CANCEL;
}
int code= DebugUITools.openLaunchConfigurationPropertiesDialog(shell, config, org.eclipse.ui.externaltools.internal.model.IExternalToolConstants.ID_EXTERNAL_TOOLS_BUILDER_LAUNCH_GROUP);
manager.removeLaunchConfigurationListener(configurationListener);
return code;
}
/**
* Prompts the user to choose a launch configuration type to create and
* returns the type the user selected or <code>null</code> if the user
* cancelled.
*
* @return the configuration type selected by the user or <code>null</code>
* if the user cancelled.
*/
private ILaunchConfigurationType promptForConfigurationType() {
List<ILaunchConfigurationType> externalToolTypes = getConfigurationTypes(IExternalToolConstants.ID_EXTERNAL_TOOLS_BUILDER_LAUNCH_CATEGORY);
Shell shell= getShell();
if (shell == null) {
return null;
}
ElementListSelectionDialog dialog = new ElementListSelectionDialog(shell, new BuilderLabelProvider());
dialog.setElements(externalToolTypes.toArray());
dialog.setMultipleSelection(false);
dialog.setTitle(ExternalToolsUIMessages.BuilderPropertyPage_Choose_configuration_type_8);
dialog.setMessage(ExternalToolsUIMessages.BuilderPropertyPage_Choose_an_external_tool_type_to_create_9);
dialog.open();
Object result[] = dialog.getResult();
if (result == null || result.length == 0) {
return null;
}
return (ILaunchConfigurationType) result[0];
}
/**
* Returns the launch configuration types of the given category
*/
private List<ILaunchConfigurationType> getConfigurationTypes(String category) {
ILaunchConfigurationType types[] = DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurationTypes();
List<ILaunchConfigurationType> externalToolTypes = new ArrayList<>();
for (ILaunchConfigurationType configurationType : types) {
if (category.equals(configurationType.getCategory())) {
externalToolTypes.add(configurationType);
}
}
return externalToolTypes;
}
/**
* The user has pressed the edit button or double-clicked. Open the launch configuration edit
* dialog on the selection after migrating the tool if necessary.
*/
private void handleEditButtonPressed() {
TableItem[] items= viewer.getTable().getSelection();
if (items.length == 0) {
return;
}
TableItem selection= items[0];
if (selection != null) {
Object data = selection.getData();
if (data instanceof ILaunchConfiguration) {
ILaunchConfiguration config= (ILaunchConfiguration) data;
if (BuilderUtils.isUnmigratedConfig(config)) {
if (!shouldProceedWithMigration()) {
return;
}
try {
config= BuilderUtils.migrateBuilderConfiguration(getInputProject(), (ILaunchConfigurationWorkingCopy) config);
} catch (CoreException e) {
handleException(e);
return;
}
// Replace the working copy in the table with the migrated configuration
selection.setData(config);
}
userHasMadeChanges= true;
boolean wasAutobuilding= ResourcesPlugin.getWorkspace().getDescription().isAutoBuilding();
try {
setAutobuild(false);
editConfiguration(config);
} catch (CoreException e) {
handleException(e);
} finally {
try {
setAutobuild(wasAutobuilding);
} catch (CoreException e) {
handleException(e);
}
}
} else if (data instanceof ICommand) {
ICommand command= (ICommand) data;
if (command.isConfigurable()) {
if (editCommand(command)) {
userHasMadeChanges= true;
}
}
}
}
}
private boolean editCommand(ICommand data) {
EditCommandDialog dialog= new EditCommandDialog(getShell(), data);
return Window.OK == dialog.open();
}
/**
* Prompts the user to proceed with the migration of a project builder from
* the old format to the new, launch configuration-based, format and returns
* whether or not the user wishes to proceed with the migration.
*
* @return boolean whether or not the user wishes to proceed with migration
*/
private boolean shouldProceedWithMigration() {
if (!ExternalToolsPlugin.getDefault().getPreferenceStore().getBoolean(IPreferenceConstants.PROMPT_FOR_TOOL_MIGRATION)) {
// User has asked not to be prompted
return true;
}
Shell shell= getShell();
if (shell == null) {
return false;
}
// Warn the user that editing an old config will cause storage migration.
MessageDialogWithToggle dialog= MessageDialogWithToggle.openYesNoQuestion(getShell(),
ExternalToolsUIMessages.BuilderPropertyPage_Migrate_project_builder_10,
ExternalToolsUIMessages.BuilderPropertyPage_Not_Support,
ExternalToolsUIMessages.BuilderPropertyPage_Prompt,
false,
ExternalToolsPlugin.getDefault().getPreferenceStore(),
IPreferenceConstants.PROMPT_FOR_TOOL_MIGRATION);
return dialog.getReturnCode() == IDialogConstants.YES_ID;
}
/**
* Handles unexpected internal exceptions
*/
private void handleException(Exception e) {
final IStatus[] status= new IStatus[1];
if (e instanceof CoreException) {
status[0] = ((CoreException) e).getStatus();
} else {
status[0] = new Status(IStatus.ERROR, ExternalToolsPlugin.PLUGIN_ID, 0, ExternalToolsUIMessages.BuilderPropertyPage_statusMessage, e);
}
Display.getDefault().asyncExec(() -> {
Shell shell = getShell();
if (shell != null) {
ErrorDialog.openError(shell, ExternalToolsUIMessages.BuilderPropertyPage_errorTitle, ExternalToolsUIMessages.BuilderPropertyPage_errorMessage, status[0]);
}
});
}
/**
* The user has selected a different builder in table.
* Update button enablement.
*/
private void handleTableSelectionChanged() {
newButton.setEnabled(true);
Table builderTable= viewer.getTable();
TableItem[] items = builderTable.getSelection();
fCanEdit = false;
boolean enableRemove = false;
boolean enableUp = false;
boolean enableDown = false;
if(items != null) {
boolean validSelection = items.length > 0;
fCanEdit = validSelection;
enableRemove = validSelection;
enableUp = validSelection;
enableDown = validSelection;
if (items.length > 1) {
fCanEdit= false;
}
int indices[]= builderTable.getSelectionIndices();
int max = builderTable.getItemCount();
if(indices.length > 0) {
enableUp = indices[0] != 0;
enableDown = indices[indices.length - 1] < max - 1;
}
for (TableItem item : items) {
Object data= item.getData();
if (data instanceof ILaunchConfiguration) {
ILaunchConfiguration config= (ILaunchConfiguration)data;
String builderName= null;
try {
builderName = config.getAttribute(IExternalToolConstants.ATTR_DISABLED_BUILDER, (String)null);
} catch (CoreException e) {
}
if (builderName != null) {
//do not allow "wrapped" builders to be removed or edited if they are valid
IExtension ext= Platform.getExtensionRegistry().getExtension(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_BUILDERS, builderName);
fCanEdit= false;
enableRemove= ext == null;
}
} else {
if (data instanceof ErrorConfig) {
fCanEdit= false;
continue;
}
ICommand command= (ICommand) data;
fCanEdit= command.isConfigurable();
IExtension ext= Platform.getExtensionRegistry().getExtension(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_BUILDERS, command.getBuilderName());
enableRemove= ext == null;
break;
}
}
}
editButton.setEnabled(fCanEdit);
removeButton.setEnabled(enableRemove);
upButton.setEnabled(enableUp);
downButton.setEnabled(enableDown);
}
/**
* Returns whether the given element (command or launch config)
* is enabled.
*
* @param element the element
* @return whether the given element is enabled
*/
private boolean isEnabled(Object element) {
if (element instanceof ICommand) {
ICommand command = (ICommand) element;
String val = command.getArguments().get(COMMAND_ENABLED);
if(val != null) {
//null means enabled, see #doPerformOk
return Boolean.parseBoolean(val);
}
} else if (element instanceof ILaunchConfiguration) {
try {
return ExternalToolsUtil.isBuilderEnabled((ILaunchConfiguration) element);
} catch (CoreException e) {
}
} else if (element instanceof ErrorConfig) {
return false;
}
return true;
}
/**
* Moves an entry in the builder table to the given index.
*/
private void move(TableItem item, int index) {
userHasMadeChanges= true;
Object data = item.getData();
item.dispose();
viewer.insert(data, index);
viewer.setChecked(data, isEnabled(data));
}
/**
* Move the current selection in the build list down.
*/
private void moveSelectionDown() {
Table builderTable= viewer.getTable();
int indices[]= builderTable.getSelectionIndices();
if (indices.length < 1) {
return;
}
int newSelection[]= new int[indices.length];
int max= builderTable.getItemCount() - 1;
for (int i = indices.length - 1; i >= 0; i--) {
int index= indices[i];
if (index < max) {
move (builderTable.getItem(index), index + 1);
newSelection[i]= index + 1;
}
}
builderTable.setSelection(newSelection);
}
/**
* Move the current selection in the build list up.
*/
private void moveSelectionUp() {
Table builderTable= viewer.getTable();
int indices[]= builderTable.getSelectionIndices();
int newSelection[]= new int[indices.length];
for (int i = 0; i < indices.length; i++) {
int index= indices[i];
if (index > 0) {
move (builderTable.getItem(index), index - 1);
newSelection[i]= index - 1;
}
}
builderTable.setSelection(newSelection);
}
@Override
public boolean performOk() {
if (!userHasMadeChanges) {
return super.performOk();
}
userHasMadeChanges= false;
Table builderTable= viewer.getTable();
int numCommands = builderTable.getItemCount();
final Object[] itemData= new Object[numCommands];
for (int i = 0; i < numCommands; i++) {
itemData[i]= builderTable.getItem(i).getData();
}
IRunnableWithProgress runnable = monitor -> {
doPerformOk(monitor, itemData);
if (monitor.isCanceled()) {
throw new InterruptedException();
}
};
IProgressService service= PlatformUI.getWorkbench().getProgressService();
try {
service.busyCursorWhile(runnable);
} catch (InvocationTargetException | InterruptedException e) {
return false;
}
return super.performOk();
}
private void doPerformOk(IProgressMonitor monitor, Object[] itemData) {
if (monitor.isCanceled()) {
return;
}
IProject project = getInputProject();
//get all the build commands
int numCommands = itemData.length;
monitor.beginTask(ExternalToolsUIMessages.BuilderPropertyPage_3, numCommands + 1);
List<ICommand> possibleCommands = new ArrayList<>(numCommands);
for (int i = 0; i < numCommands; i++) {
Object data = itemData[i];
if (data instanceof ICommand) {
if (commandsToBeDeleted != null && commandsToBeDeleted.contains(data)) {
//command specified to be removed
data= null;
continue;
}
ICommand command= (ICommand)data;
Map<String, String> args = command.getArguments();
String val = args.get(COMMAND_ENABLED);
if(val != null) {
if (!Boolean.parseBoolean(val)) {
ILaunchConfiguration config= disableCommand(command);
if (config != null) {
data= BuilderUtils.commandFromLaunchConfig(project,config);
}
} else {
args.remove(COMMAND_ENABLED);
command.setArguments(args);
}
}
} else if (data instanceof ILaunchConfiguration) {
ILaunchConfiguration config= (ILaunchConfiguration) data;
String disabledBuilderName;
try {
disabledBuilderName = config.getAttribute(IExternalToolConstants.ATTR_DISABLED_BUILDER, (String)null);
if (disabledBuilderName != null && ExternalToolsUtil.isBuilderEnabled(config)) {
possibleCommands.add(translateBackToCommand(config, project));
continue;
}
} catch (CoreException e1) {
}
if (!BuilderUtils.isUnmigratedConfig(config) && (config instanceof ILaunchConfigurationWorkingCopy)) {
ILaunchConfigurationWorkingCopy workingCopy= ((ILaunchConfigurationWorkingCopy) config);
// Save any changes to the config (such as enable/disable)
if (workingCopy.isDirty()) {
try {
workingCopy.doSave();
} catch (CoreException e) {
Shell shell= getShell();
if (shell != null) {
MessageDialog.openError(shell, ExternalToolsUIMessages.BuilderPropertyPage_39, NLS.bind(ExternalToolsUIMessages.BuilderPropertyPage_40, new String[] {workingCopy.getName()}));
}
}
}
}
data= BuilderUtils.commandFromLaunchConfig(project, config);
} else if (data instanceof ErrorConfig) {
data= ((ErrorConfig) data).getCommand();
}
if (data instanceof ICommand) {
possibleCommands.add((ICommand) data);
}
monitor.worked(1);
}
ICommand[] commands= new ICommand[possibleCommands.size()];
possibleCommands.toArray(commands);
if (checkCommandsForChange(commands)) {
//set the build spec
try {
IProjectDescription desc = project.getDescription();
desc.setBuildSpec(commands);
project.setDescription(desc, IResource.FORCE, monitor);
} catch (CoreException e) {
handleException(e);
performCancel();
}
}
if (configsToBeDeleted != null) {
deleteConfigurations();
}
monitor.done();
}
private void checkBuilderFolder() {
try {
IFolder builderFolder= BuilderUtils.getBuilderFolder(getInputProject(), false);
if (builderFolder != null && builderFolder.exists() && builderFolder.members().length == 0) {
// All files in the builder folder have been deleted. Clean up
builderFolder.delete(true, false, null);
}
} catch (CoreException e) {
handleException(e);
}
}
/**
* A non-external tool builder builder was disabled.
* It has been re-enabled. Translate the disabled external tool builder launch configuration
* wrapper back into the full fledged builder command.
*/
private ICommand translateBackToCommand(ILaunchConfiguration config, IProject project) {
try {
ICommand newCommand = project.getDescription().newCommand();
String builderName= config.getAttribute(IExternalToolConstants.ATTR_DISABLED_BUILDER, (String)null);
Map<String, String> args = config.getAttribute(IExternalToolConstants.ATTR_TOOL_ARGUMENTS, new HashMap<>(0));
newCommand.setBuilderName(builderName);
newCommand.setArguments(args);
if (configsToBeDeleted == null) {
configsToBeDeleted = new ArrayList<>();
}
configsToBeDeleted.add(config);
return newCommand;
} catch (CoreException exception) {
Shell shell= getShell();
if (shell != null) {
MessageDialog.openError(shell, ExternalToolsUIMessages.BuilderPropertyPage_13, ExternalToolsUIMessages.BuilderPropertyPage_error);
}
return null;
}
}
/**
* Disables a builder by wrapping the builder command as a disabled external
* tool builder. The details of the command is persisted in the launch
* configuration.
*/
private ILaunchConfiguration disableCommand(ICommand command) {
Map<String, String> arguments = command.getArguments();
if (arguments != null) {
arguments.remove(COMMAND_ENABLED);
}
List<ILaunchConfigurationType> externalToolTypes = getConfigurationTypes(IExternalToolConstants.ID_EXTERNAL_TOOLS_BUILDER_LAUNCH_CATEGORY);
if (externalToolTypes.isEmpty()) {
return null;
}
ILaunchConfigurationType type= externalToolTypes.get(0);
if (type == null) {
return null;
}
boolean wasAutobuilding= ResourcesPlugin.getWorkspace().getDescription().isAutoBuilding();
try {
ILaunchConfigurationWorkingCopy workingCopy = null;
String builderName = command.getBuilderName();
String name= DebugPlugin.getDefault().getLaunchManager().generateLaunchConfigurationName(builderName);
workingCopy = type.newInstance(BuilderUtils.getBuilderFolder(getInputProject(), true), name);
workingCopy.setAttribute(IExternalToolConstants.ATTR_DISABLED_BUILDER, builderName);
if (arguments != null) {
workingCopy.setAttribute(IExternalToolConstants.ATTR_TOOL_ARGUMENTS, arguments);
}
workingCopy.setAttribute(IExternalToolConstants.ATTR_BUILDER_ENABLED, false);
ILaunchConfiguration config = null;
setAutobuild(false);
config = workingCopy.doSave();
return config;
} catch (CoreException e) {
handleException(e);
} finally {
try {
setAutobuild(wasAutobuilding);
} catch (CoreException e) {
handleException(e);
}
}
return null;
}
private void deleteConfigurations() {
boolean wasAutobuilding= ResourcesPlugin.getWorkspace().getDescription().isAutoBuilding();
try {
setAutobuild(false);
for (ILaunchConfiguration config : configsToBeDeleted) {
config.delete();
}
checkBuilderFolder();
} catch (CoreException e) {
handleException(e);
} finally {
try {
setAutobuild(wasAutobuilding);
} catch (CoreException e) {
handleException(e);
}
}
}
/**
* Returns whether any of the commands have changed.
*/
private boolean checkCommandsForChange(ICommand[] newCommands) {
try {
ICommand[] oldCommands = getInputProject().getDescription().getBuildSpec();
if (oldCommands.length != newCommands.length) {
return true;
}
IgnoreWhiteSpaceComparator comparator= new IgnoreWhiteSpaceComparator();
for (int i = 0; i < oldCommands.length; i++) {
ICommand oldCommand = oldCommands[i];
ICommand newCommand= newCommands[i];
String oldName= oldCommand.getBuilderName();
String newName= newCommand.getBuilderName();
if (oldName == null && newName != null) {
return true;
}
if(oldName != null && !oldName.equals(newName)) {
return true;
}
Map<String, String> oldArgs = oldCommand.getArguments();
Map<String, String> newArgs = newCommand.getArguments();
if (oldArgs == null) {
if(newArgs != null) {
return true;
}
continue;
}
if(oldArgs.size() != newArgs.size()) {
return true;
}
for (Map.Entry<String, String> entry : oldArgs.entrySet()) {
String key = entry.getKey();
if (comparator.compare(entry.getValue(), newArgs.get(key)) != 0) {
return true;
}
}
if (oldCommand.isBuilding(IncrementalProjectBuilder.AUTO_BUILD) != newCommand.isBuilding(IncrementalProjectBuilder.AUTO_BUILD)
|| oldCommand.isBuilding(IncrementalProjectBuilder.CLEAN_BUILD) != newCommand.isBuilding(IncrementalProjectBuilder.CLEAN_BUILD)
|| oldCommand.isBuilding(IncrementalProjectBuilder.INCREMENTAL_BUILD) != newCommand.isBuilding(IncrementalProjectBuilder.INCREMENTAL_BUILD)
|| oldCommand.isBuilding(IncrementalProjectBuilder.FULL_BUILD) != newCommand.isBuilding(IncrementalProjectBuilder.FULL_BUILD)) {
return true;
}
}
} catch (CoreException ce) {
return true;
}
return false;
}
@Override
public boolean performCancel() {
for (ILaunchConfiguration config : newConfigList) {
try {
config.delete();
} catch (CoreException e) {
handleException(e);
}
}
checkBuilderFolder();
//remove the local marking of the enabled state of the commands
Table builderTable= viewer.getTable();
int numCommands = builderTable.getItemCount();
for (int i = 0; i < numCommands; i++) {
Object data = builderTable.getItem(i).getData();
if (data instanceof ICommand) {
ICommand command= (ICommand)data;
Map<String, String> args = command.getArguments();
args.remove(COMMAND_ENABLED);
command.setArguments(args);
}
}
return super.performCancel();
}
@Override
public Shell getShell() {
if (getControl().isDisposed()) {
return null;
}
return super.getShell();
}
}