| /******************************************************************************* |
| * Copyright (c) 2007, 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 |
| * Mickael Istria (Red Hat Inc.) - [525882] Delete nested projects |
| *******************************************************************************/ |
| package org.eclipse.ltk.ui.refactoring.resource; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.net.URI; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.osgi.util.NLS; |
| |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.StyledText; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.Point; |
| 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.Label; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| |
| import org.eclipse.jface.dialogs.Dialog; |
| import org.eclipse.jface.dialogs.IDialogConstants; |
| import org.eclipse.jface.layout.LayoutConstants; |
| import org.eclipse.jface.wizard.IWizardPage; |
| |
| import org.eclipse.ltk.core.refactoring.participants.DeleteRefactoring; |
| import org.eclipse.ltk.core.refactoring.resource.Resources; |
| import org.eclipse.ltk.internal.core.refactoring.resource.DeleteResourcesProcessor; |
| import org.eclipse.ltk.internal.ui.refactoring.BasicElementLabels; |
| import org.eclipse.ltk.internal.ui.refactoring.Messages; |
| import org.eclipse.ltk.internal.ui.refactoring.RefactoringUIMessages; |
| import org.eclipse.ltk.internal.ui.refactoring.RefactoringUIPlugin; |
| import org.eclipse.ltk.ui.refactoring.RefactoringWizard; |
| import org.eclipse.ltk.ui.refactoring.UserInputWizardPage; |
| |
| /** |
| * A wizard for the delete resources refactoring. |
| * |
| * @since 3.4 |
| */ |
| public class DeleteResourcesWizard extends RefactoringWizard { |
| |
| /** |
| * Creates a {@link DeleteResourcesWizard} |
| * |
| * @param resources the resources to delete |
| */ |
| public DeleteResourcesWizard(IResource[] resources) { |
| super(new DeleteRefactoring(new DeleteResourcesProcessor(resources, false)), DIALOG_BASED_USER_INTERFACE); |
| setDefaultPageTitle(RefactoringUIMessages.DeleteResourcesWizard_page_title); |
| setWindowTitle(RefactoringUIMessages.DeleteResourcesWizard_window_title); |
| } |
| |
| @Override |
| protected void addUserInputPages() { |
| DeleteResourcesProcessor processor= getRefactoring().getAdapter(DeleteResourcesProcessor.class); |
| addPage(new DeleteResourcesRefactoringConfigurationPage(processor)); |
| } |
| |
| private static class DeleteResourcesRefactoringConfigurationPage extends UserInputWizardPage { |
| |
| private DeleteResourcesProcessor fRefactoringProcessor; |
| |
| private Button fDeleteContentsButton; |
| |
| private StyledText fProjectLocationsList; |
| |
| private Label fProjectLocationsLabel; |
| |
| public DeleteResourcesRefactoringConfigurationPage(DeleteResourcesProcessor processor) { |
| super("DeleteResourcesRefactoringConfigurationPage"); //$NON-NLS-1$ |
| fRefactoringProcessor= processor; |
| } |
| |
| @Override |
| public void createControl(Composite parent) { |
| initializeDialogUnits(parent); |
| |
| Point defaultSpacing= LayoutConstants.getSpacing(); |
| |
| Composite composite= new Composite(parent, SWT.NONE); |
| GridLayout gridLayout= new GridLayout(2, false); |
| gridLayout.horizontalSpacing= defaultSpacing.x * 2; |
| gridLayout.verticalSpacing= defaultSpacing.y; |
| |
| composite.setLayout(gridLayout); |
| composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); |
| |
| Image image= parent.getDisplay().getSystemImage(SWT.ICON_QUESTION); |
| Label imageLabel = new Label(composite, SWT.NULL); |
| if (image != null) { |
| imageLabel.setBackground(image.getBackground()); |
| imageLabel.setImage(image); |
| } |
| imageLabel.setLayoutData(new GridData(SWT.CENTER, SWT.BEGINNING, false, false)); |
| |
| final IResource[] initialResources= fRefactoringProcessor.getResourcesToDelete(); |
| Label label= new Label(composite, SWT.WRAP); |
| |
| boolean onlyProjects= Resources.containsOnlyProjects(initialResources); |
| if (onlyProjects) { |
| if (initialResources.length == 1) { |
| label.setText(Messages.format(RefactoringUIMessages.DeleteResourcesWizard_label_single_project, BasicElementLabels.getResourceName(initialResources[0]))); |
| } else { |
| label.setText(Messages.format(RefactoringUIMessages.DeleteResourcesWizard_label_multi_projects, Integer.valueOf(initialResources.length))); |
| } |
| } else if (containsLinkedResource(initialResources)) { |
| if (initialResources.length == 1) { |
| label.setText(Messages.format(RefactoringUIMessages.DeleteResourcesWizard_label_single_linked, BasicElementLabels.getResourceName(initialResources[0]))); |
| } else { |
| label.setText(Messages.format(RefactoringUIMessages.DeleteResourcesWizard_label_multi_linked, Integer.valueOf(initialResources.length))); |
| } |
| } else { |
| if (initialResources.length == 1) { |
| label.setText(Messages.format(RefactoringUIMessages.DeleteResourcesWizard_label_single, BasicElementLabels.getResourceName(initialResources[0]))); |
| } else { |
| label.setText(Messages.format(RefactoringUIMessages.DeleteResourcesWizard_label_multi, Integer.valueOf(initialResources.length))); |
| } |
| } |
| GridData gridData= new GridData(SWT.FILL, SWT.FILL, true, false); |
| gridData.widthHint= convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH); |
| label.setLayoutData(gridData); |
| |
| Composite supportArea= new Composite(composite, SWT.NONE); |
| supportArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); |
| gridLayout= new GridLayout(1, false); |
| gridLayout.horizontalSpacing= defaultSpacing.x * 2; |
| gridLayout.verticalSpacing= defaultSpacing.y; |
| |
| supportArea.setLayout(gridLayout); |
| |
| if (onlyProjects) { |
| Set<IProject> nestedProjects = computeNestedProjects(initialResources); |
| fDeleteContentsButton= new Button(supportArea, SWT.CHECK); |
| fDeleteContentsButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); |
| fDeleteContentsButton.setText(RefactoringUIMessages.DeleteResourcesWizard_project_deleteContents); |
| fDeleteContentsButton.setFocus(); |
| |
| if (!nestedProjects.isEmpty()) { |
| Set<IResource> projectHierarchy = new HashSet<>(initialResources.length + nestedProjects.size(), (float)1.0); |
| projectHierarchy.addAll(Arrays.asList(fRefactoringProcessor.getResourcesToDelete())); |
| projectHierarchy.addAll(nestedProjects); |
| Button deleteNestedProjectsCheckbox = new Button(supportArea, SWT.CHECK); |
| deleteNestedProjectsCheckbox.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); |
| deleteNestedProjectsCheckbox.setText( |
| nestedProjects.size() == 1 ? |
| RefactoringUIMessages.DeleteResourcesWizard_label_alsoDeleteOneNestedProject : |
| NLS.bind(RefactoringUIMessages.DeleteResourcesWizard_label_alsoDeleteNestedProjects, nestedProjects.size())); |
| SelectionAdapter deleteNestedProjectsCheckboxListener = new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| final boolean deleteNestedProjects = deleteNestedProjectsCheckbox.getSelection(); |
| try { |
| getContainer().run(false, true, pm -> { |
| try { |
| fRefactoringProcessor.setResources(deleteNestedProjects ? projectHierarchy.toArray(new IResource[projectHierarchy.size()]) : initialResources, pm); |
| } catch (OperationCanceledException | CoreException ex) { |
| throw new InvocationTargetException(ex); |
| } |
| }); |
| updateListOfProjects(); |
| } catch (InvocationTargetException | InterruptedException ex) { |
| RefactoringUIPlugin.log(ex); |
| } |
| } |
| }; |
| deleteNestedProjectsCheckbox.addSelectionListener(deleteNestedProjectsCheckboxListener); |
| |
| fDeleteContentsButton.addSelectionListener(new SelectionAdapter() { |
| private boolean previousNestedProjectSelection; |
| |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| if (fDeleteContentsButton.getSelection()) { |
| previousNestedProjectSelection = deleteNestedProjectsCheckbox.getSelection(); |
| deleteNestedProjectsCheckbox.setSelection(true); |
| } else { |
| deleteNestedProjectsCheckbox.setSelection(previousNestedProjectSelection); |
| } |
| deleteNestedProjectsCheckbox.setEnabled(!fDeleteContentsButton.getSelection()); |
| super.widgetSelected(e); |
| } |
| }); |
| fDeleteContentsButton.addSelectionListener(deleteNestedProjectsCheckboxListener); |
| } |
| |
| fProjectLocationsLabel= new Label(supportArea, SWT.NONE); |
| GridData labelData= new GridData(SWT.FILL, SWT.FILL, true, false); |
| labelData.verticalIndent= 5; |
| fProjectLocationsLabel.setLayoutData(labelData); |
| |
| int style= SWT.MULTI | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL; |
| if (initialResources.length > 1 || !nestedProjects.isEmpty()) |
| style |= SWT.BORDER; |
| fProjectLocationsList= new StyledText(supportArea, style); |
| fProjectLocationsList.setAlwaysShowScrollBars(false); |
| labelData.horizontalIndent= fProjectLocationsList.getLeftMargin(); |
| gridData= new GridData(SWT.FILL, SWT.FILL, true, true); |
| gridData.heightHint= convertHeightInCharsToPixels(Math.min(initialResources.length + nestedProjects.size(), 5)); |
| fProjectLocationsList.setLayoutData(gridData); |
| fProjectLocationsList.setBackground(fProjectLocationsList.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND)); |
| Dialog.applyDialogFont(fProjectLocationsList); |
| |
| updateListOfProjects(); |
| } |
| setControl(composite); |
| } |
| |
| private void updateListOfProjects() { |
| IResource[] initialResources = fRefactoringProcessor.getResourcesToDelete(); |
| StringBuilder buf= new StringBuilder(); |
| for (IResource initialResource : initialResources) { |
| String location= getLocation(initialResource); |
| if (location != null) { |
| if (buf.length() > 0) |
| buf.append('\n'); |
| buf.append(location); |
| } |
| } |
| fProjectLocationsList.setText(buf.toString()); |
| fProjectLocationsLabel.setText(initialResources.length == 1 |
| ? RefactoringUIMessages.DeleteResourcesWizard_project_location |
| : NLS.bind(RefactoringUIMessages.DeleteResourcesWizard_project_locations, initialResources.length)); |
| fProjectLocationsList.getParent().pack(true); |
| fProjectLocationsList.getParent().requestLayout(); |
| } |
| |
| private static String getLocation(IResource resource) { |
| IPath location= resource.getLocation(); |
| if (location != null) |
| return BasicElementLabels.getPathLabel(location, true); |
| |
| URI uri= resource.getLocationURI(); |
| if (uri != null) |
| return BasicElementLabels.getURLPart(uri.toString()); |
| |
| URI rawLocationURI= resource.getRawLocationURI(); |
| if (rawLocationURI != null) |
| return BasicElementLabels.getURLPart(rawLocationURI.toString()); |
| |
| return BasicElementLabels.getResourceName(resource); |
| } |
| |
| private boolean containsLinkedResource(IResource[] resources) { |
| for (IResource resource : resources) { |
| if (resource != null && resource.isLinked()) { // paranoia code, can not be null |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| protected boolean performFinish() { |
| initializeRefactoring(); |
| storeSettings(); |
| return super.performFinish(); |
| } |
| |
| @Override |
| public IWizardPage getNextPage() { |
| initializeRefactoring(); |
| storeSettings(); |
| return super.getNextPage(); |
| } |
| |
| private void initializeRefactoring() { |
| fRefactoringProcessor.setDeleteContents(fDeleteContentsButton == null ? false : fDeleteContentsButton.getSelection()); |
| } |
| |
| private void storeSettings() { |
| } |
| } |
| |
| private static Set<IProject> computeNestedProjects(final IResource[] initialResources) { |
| if (initialResources == null) { |
| return Collections.emptySet(); |
| } |
| final List<IResource> resources = new ArrayList<>(Arrays.asList(initialResources)); |
| resources.removeIf(res -> res.getLocation() == null); |
| final Comparator<IResource> pathComparator = (res1, res2) -> res1.getLocation().toString().compareTo(res2.getLocation().toString()); |
| resources.sort(pathComparator); |
| if (resources.isEmpty()) { |
| return Collections.emptySet(); |
| } |
| // need to be sorted |
| final List<IProject> allProjects = new ArrayList<>(Arrays.asList(initialResources[0].getWorkspace().getRoot().getProjects())); |
| allProjects.removeIf(project -> project.getLocation() == null); |
| allProjects.sort(pathComparator); |
| int resourceIndex = 0; |
| int projectIndex = 0; |
| final Set<IProject> res = new HashSet<>(); |
| while (resourceIndex < resources.size() && projectIndex < allProjects.size()) { |
| final IPath resourcePath = resources.get(resourceIndex).getLocation(); |
| final IProject project = allProjects.get(projectIndex); |
| final IPath projectPath = project.getLocation(); |
| if (resourcePath.isPrefixOf(projectPath)) { |
| res.add(project); |
| projectIndex++; |
| continue; |
| } |
| int delta = resourcePath.toString().compareTo(projectPath.toString()); |
| if (delta < 0) { |
| resourceIndex++; |
| } else { |
| projectIndex++; |
| } |
| } |
| res.removeAll(resources); |
| return res; |
| } |
| } |