1 /*******************************************************************************
2 * Copyright (c) 2000, 2011 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.corext.refactoring.reorg;
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.Collections;
16 import java.util.Comparator;
17 import java.util.HashMap;
18 import java.util.HashSet;
19 import java.util.Iterator;
20 import java.util.List;
24 import org.eclipse.core.runtime.Assert;
25 import org.eclipse.core.runtime.CoreException;
26 import org.eclipse.core.runtime.IAdaptable;
27 import org.eclipse.core.runtime.IProgressMonitor;
28 import org.eclipse.core.runtime.OperationCanceledException;
30 import org.eclipse.core.resources.IContainer;
31 import org.eclipse.core.resources.IFile;
32 import org.eclipse.core.resources.IFolder;
33 import org.eclipse.core.resources.IProject;
34 import org.eclipse.core.resources.IResource;
35 import org.eclipse.core.resources.IResourceVisitor;
36 import org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory;
38 import org.eclipse.core.filebuffers.FileBuffers;
39 import org.eclipse.core.filebuffers.ITextFileBuffer;
40 import org.eclipse.core.filebuffers.LocationKind;
42 import org.eclipse.ltk.core.refactoring.Change;
43 import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
44 import org.eclipse.ltk.core.refactoring.RefactoringStatus;
45 import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
46 import org.eclipse.ltk.core.refactoring.participants.DeleteProcessor;
47 import org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant;
48 import org.eclipse.ltk.core.refactoring.participants.ResourceChangeChecker;
49 import org.eclipse.ltk.core.refactoring.participants.SharableParticipants;
51 import org.eclipse.jdt.core.ICompilationUnit;
52 import org.eclipse.jdt.core.IField;
53 import org.eclipse.jdt.core.IJavaElement;
54 import org.eclipse.jdt.core.IJavaProject;
55 import org.eclipse.jdt.core.IMethod;
56 import org.eclipse.jdt.core.IPackageFragment;
57 import org.eclipse.jdt.core.IPackageFragmentRoot;
58 import org.eclipse.jdt.core.IType;
59 import org.eclipse.jdt.core.JavaCore;
60 import org.eclipse.jdt.core.JavaModelException;
61 import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
62 import org.eclipse.jdt.core.refactoring.descriptors.DeleteDescriptor;
63 import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
65 import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory;
66 import org.eclipse.jdt.internal.corext.codemanipulation.GetterSetterUtil;
67 import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
68 import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
69 import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
70 import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester;
71 import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
72 import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
73 import org.eclipse.jdt.internal.corext.refactoring.participants.JavaProcessors;
74 import org.eclipse.jdt.internal.corext.refactoring.participants.ResourceProcessors;
75 import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil;
76 import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
77 import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager;
78 import org.eclipse.jdt.internal.corext.util.Messages;
79 import org.eclipse.jdt.internal.corext.util.Resources;
81 import org.eclipse.jdt.ui.JavaElementLabels;
82 import org.eclipse.jdt.ui.refactoring.IRefactoringProcessorIds;
84 import org.eclipse.jdt.internal.ui.JavaPlugin;
85 import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
87 public final class JavaDeleteProcessor extends DeleteProcessor {
89 private static final String ATTRIBUTE_RESOURCES= "resources"; //$NON-NLS-1$
90 private static final String ATTRIBUTE_ELEMENTS= "elements"; //$NON-NLS-1$
91 private static final String ATTRIBUTE_SUGGEST_ACCESSORS= "accessors"; //$NON-NLS-1$
92 private static final String ATTRIBUTE_DELETE_SUBPACKAGES= "subPackages"; //$NON-NLS-1$
94 private boolean fAccessorsDeleted;
95 private boolean fWasCanceled;
96 private boolean fSuggestGetterSetterDeletion;
97 private Object[] fElements;
98 private IResource[] fResources;
99 private IJavaElement[] fJavaElements;
100 private IReorgQueries fDeleteQueries;
101 private DeleteModifications fDeleteModifications;
103 private Change fDeleteChange;
104 private boolean fDeleteSubPackages;
106 public JavaDeleteProcessor(Object[] elements) {
108 if (fElements != null) {
109 fResources= RefactoringAvailabilityTester.getResources(elements);
110 fJavaElements= RefactoringAvailabilityTester.getJavaElements(elements);
112 fSuggestGetterSetterDeletion= true;
113 fDeleteSubPackages= false;
117 public JavaDeleteProcessor(JavaRefactoringArguments arguments, RefactoringStatus status) {
119 RefactoringStatus initStatus= initialize(arguments);
120 status.merge(initStatus);
124 public String getIdentifier() {
125 return IRefactoringProcessorIds.DELETE_PROCESSOR;
129 public boolean isApplicable() throws CoreException {
130 if (fElements.length == 0)
132 if (fElements.length != fResources.length + fJavaElements.length)
134 for (int i= 0; i < fResources.length; i++) {
135 if (!RefactoringAvailabilityTester.isDeleteAvailable(fResources[i]))
138 for (int i= 0; i < fJavaElements.length; i++) {
139 if (!RefactoringAvailabilityTester.isDeleteAvailable(fJavaElements[i]))
145 public boolean needsProgressMonitor() {
146 if (fResources != null && fResources.length > 0)
148 if (fJavaElements != null) {
149 for (int i= 0; i < fJavaElements.length; i++) {
150 int type= fJavaElements[i].getElementType();
151 if (type <= IJavaElement.CLASS_FILE)
160 public String getProcessorName() {
161 return RefactoringCoreMessages.DeleteRefactoring_7;
165 public Object[] getElements() {
170 public RefactoringParticipant[] loadParticipants(RefactoringStatus status, SharableParticipants shared) throws CoreException {
171 return fDeleteModifications.loadParticipants(status, this, getAffectedProjectNatures(), shared);
174 private String[] getAffectedProjectNatures() throws CoreException {
175 String[] jNatures= JavaProcessors.computeAffectedNaturs(fJavaElements);
176 String[] rNatures= ResourceProcessors.computeAffectedNatures(fResources);
177 Set<String> result= new HashSet<String>();
178 result.addAll(Arrays.asList(jNatures));
179 result.addAll(Arrays.asList(rNatures));
180 return result.toArray(new String[result.size()]);
184 * This has to be customizable because when drag and drop is performed on a field,
185 * you don't want to suggest deleting getter/setter if only the field was moved.
187 public void setSuggestGetterSetterDeletion(boolean suggest){
188 fSuggestGetterSetterDeletion= suggest;
191 public void setDeleteSubPackages(boolean selection) {
192 fDeleteSubPackages= selection;
195 public boolean getDeleteSubPackages() {
196 return fDeleteSubPackages;
199 public boolean hasSubPackagesToDelete() {
201 for (int i= 0; i < fJavaElements.length; i++) {
202 if (fJavaElements[i] instanceof IPackageFragment) {
203 IPackageFragment packageFragment= (IPackageFragment) fJavaElements[i];
204 if (packageFragment.isDefaultPackage())
205 continue; // see bug 132576 (can remove this if(..) continue; statement when bug is fixed)
206 if (packageFragment.hasSubpackages())
210 } catch (JavaModelException e) {
216 public void setQueries(IReorgQueries queries){
217 Assert.isNotNull(queries);
218 fDeleteQueries= queries;
221 public IJavaElement[] getJavaElementsToDelete(){
222 return fJavaElements;
225 public boolean wasCanceled() {
229 public IResource[] getResourcesToDelete(){
234 * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkActivation(org.eclipse.core.runtime.IProgressMonitor)
237 public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException {
238 Assert.isNotNull(fDeleteQueries);//must be set before checking activation
239 RefactoringStatus result= new RefactoringStatus();
240 result.merge(RefactoringStatus.create(Resources.checkInSync(ReorgUtils.getNotLinked(fResources))));
241 IResource[] javaResources= ReorgUtils.getResources(fJavaElements);
242 result.merge(RefactoringStatus.create(Resources.checkInSync(ReorgUtils.getNotNulls(javaResources))));
243 for (int i= 0; i < fJavaElements.length; i++) {
244 IJavaElement element= fJavaElements[i];
245 if (element instanceof IType && ((IType)element).isAnonymous()) {
246 // work around for bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=44450
247 // result.addFatalError("Currently, there isn't any support to delete an anonymous type.");
254 * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkInput(org.eclipse.core.runtime.IProgressMonitor)
257 public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditionsContext context) throws CoreException {
258 pm.beginTask(RefactoringCoreMessages.DeleteRefactoring_1, 1);
261 RefactoringStatus result= new RefactoringStatus();
263 recalculateElementsToDelete();
265 checkDirtyCompilationUnits(result);
266 checkDirtyResources(result);
267 fDeleteModifications= new DeleteModifications();
268 fDeleteModifications.delete(fResources);
269 fDeleteModifications.delete(fJavaElements);
270 List<IResource> packageDeletes= fDeleteModifications.postProcess();
272 TextChangeManager manager= new TextChangeManager();
273 fDeleteChange= DeleteChangeCreator.createDeleteChange(manager, fResources, fJavaElements, getProcessorName(), packageDeletes);
275 ResourceChangeChecker checker= (ResourceChangeChecker) context.getChecker(ResourceChangeChecker.class);
276 IResourceChangeDescriptionFactory deltaFactory= checker.getDeltaFactory();
277 fDeleteModifications.buildDelta(deltaFactory);
278 IFile[] files= ResourceUtil.getFiles(manager.getAllCompilationUnits());
279 for (int i= 0; i < files.length; i++) {
280 deltaFactory.change(files[i]);
283 } catch (OperationCanceledException e) {
291 private void checkDirtyCompilationUnits(RefactoringStatus result) throws CoreException {
292 if (fJavaElements == null || fJavaElements.length == 0)
294 for (int je= 0; je < fJavaElements.length; je++) {
295 IJavaElement element= fJavaElements[je];
296 if (element instanceof ICompilationUnit) {
297 checkDirtyCompilationUnit(result, (ICompilationUnit)element);
298 } else if (element instanceof IPackageFragment) {
299 ICompilationUnit[] units= ((IPackageFragment)element).getCompilationUnits();
300 for (int u = 0; u < units.length; u++) {
301 checkDirtyCompilationUnit(result, units[u]);
307 private void checkDirtyCompilationUnit(RefactoringStatus result, ICompilationUnit cunit) {
308 IResource resource= cunit.getResource();
309 if (resource == null || resource.getType() != IResource.FILE)
311 checkDirtyFile(result, (IFile)resource);
314 private void checkDirtyResources(final RefactoringStatus result) throws CoreException {
315 for (int i= 0; i < fResources.length; i++) {
316 IResource resource= fResources[i];
317 resource.accept(new IResourceVisitor() {
318 public boolean visit(IResource visitedResource) throws CoreException {
319 if (visitedResource instanceof IFile) {
320 checkDirtyFile(result, (IFile)visitedResource);
324 }, IResource.DEPTH_INFINITE, false);
328 private void checkDirtyFile(RefactoringStatus result, IFile file) {
329 if (file == null || !file.exists())
331 ITextFileBuffer buffer= FileBuffers.getTextFileBufferManager().getTextFileBuffer(file.getFullPath(), LocationKind.IFILE);
332 if (buffer != null && buffer.isDirty()) {
333 if (buffer.isStateValidated() && buffer.isSynchronized()) {
334 result.addWarning(Messages.format(
335 RefactoringCoreMessages.JavaDeleteProcessor_unsaved_changes,
336 BasicElementLabels.getPathLabel(file.getFullPath(), false)));
338 result.addFatalError(Messages.format(
339 RefactoringCoreMessages.JavaDeleteProcessor_unsaved_changes,
340 BasicElementLabels.getPathLabel(file.getFullPath(), false)));
346 * The set of elements that will eventually be deleted may be very different from the set
347 * originally selected - there may be fewer, more or different elements.
348 * This method is used to calculate the set of elements that will be deleted - if necessary,
351 private void recalculateElementsToDelete() throws CoreException {
352 //the sequence is critical here
353 fAccessorsDeleted= false;
354 if (fDeleteSubPackages) /* add subpackages first, to allow removing elements with parents in selection etc. */
357 removeElementsWithParentsInSelection(); /*ask before adding empty cus - you don't want to ask if you, for example delete
358 *the package, in which the cus live*/
359 removeUnconfirmedFoldersThatContainSourceFolders(); /* a selected folder may be a parent of a source folder
360 * we must inform the user about it and ask if ok to delete the folder*/
361 removeUnconfirmedReferencedArchives();
362 addEmptyCusToDelete();
363 removeJavaElementsChildrenOfJavaElements();/*because adding cus may create elements (types in cus)
364 *whose parents are in selection*/
365 confirmDeletingReadOnly(); /*after empty cus - you want to ask for all cus that are to be deleted*/
367 if (fSuggestGetterSetterDeletion)
368 addGettersSetters();/*at the end - this cannot invalidate anything*/
370 addDeletableParentPackagesOnPackageDeletion(); /* do not change the sequence in fJavaElements after this method */
374 * Adds all subpackages of the selected packages to the list of items to be
377 * @throws JavaModelException should not happen
379 private void addSubPackages() throws JavaModelException {
381 final Set<IJavaElement> javaElements= new HashSet<IJavaElement>();
382 for (int i= 0; i < fJavaElements.length; i++) {
383 if (fJavaElements[i] instanceof IPackageFragment) {
384 javaElements.addAll(Arrays.asList(JavaElementUtil.getPackageAndSubpackages((IPackageFragment) fJavaElements[i])));
386 javaElements.add(fJavaElements[i]);
390 fJavaElements= javaElements.toArray(new IJavaElement[javaElements.size()]);
394 * Add deletable parent packages to the list of items to delete.
396 * @throws CoreException should not happen
398 private void addDeletableParentPackagesOnPackageDeletion() throws CoreException {
400 final List<IPackageFragment> initialPackagesToDelete= (List<IPackageFragment>) ReorgUtils.getElementsOfType(fJavaElements, IJavaElement.PACKAGE_FRAGMENT);
402 if (initialPackagesToDelete.size() == 0)
405 // Move from inner to outer packages
406 Collections.sort(initialPackagesToDelete, new Comparator<IPackageFragment>() {
407 public int compare(IPackageFragment one, IPackageFragment two) {
408 return two.getElementName().compareTo(one.getElementName());
412 // Get resources and java elements which will be deleted as well
413 final Set<IResource> deletedChildren= new HashSet<IResource>();
414 deletedChildren.addAll(Arrays.asList(fResources));
415 for (int i= 0; i < fJavaElements.length; i++) {
416 if (!ReorgUtils.isInsideCompilationUnit(fJavaElements[i]))
417 deletedChildren.add(fJavaElements[i].getResource());
420 // new package list in the right sequence
421 final List<IPackageFragment>allFragmentsToDelete= new ArrayList<IPackageFragment>();
423 for (Iterator<IPackageFragment> outerIter= initialPackagesToDelete.iterator(); outerIter.hasNext();) {
424 final IPackageFragment currentPackageFragment= outerIter.next();
426 // The package will at least be cleared
427 allFragmentsToDelete.add(currentPackageFragment);
429 if (canRemoveCompletely(currentPackageFragment, initialPackagesToDelete)) {
431 final IPackageFragment parent= JavaElementUtil.getParentSubpackage(currentPackageFragment);
432 if (parent != null && !initialPackagesToDelete.contains(parent)) {
434 final List<IPackageFragment>emptyParents= new ArrayList<IPackageFragment>();
435 addDeletableParentPackages(parent, initialPackagesToDelete, deletedChildren, emptyParents);
437 // Add parents in the right sequence (inner to outer)
438 allFragmentsToDelete.addAll(emptyParents);
443 // Remove resources in deleted packages; and the packages as well
444 final List<IJavaElement>javaElements= new ArrayList<IJavaElement>();
445 for (int i= 0; i < fJavaElements.length; i++) {
446 if (!(fJavaElements[i] instanceof IPackageFragment)) {
447 // remove children of deleted packages
448 final IPackageFragment frag= (IPackageFragment) fJavaElements[i].getAncestor(IJavaElement.PACKAGE_FRAGMENT);
449 if (!allFragmentsToDelete.contains(frag))
450 javaElements.add(fJavaElements[i]);
453 // Re-add deleted packages - note the (new) sequence
454 javaElements.addAll(allFragmentsToDelete);
456 // Remove resources in deleted folders
457 final List<IResource>resources= new ArrayList<IResource>();
458 for (int i= 0; i < fResources.length; i++) {
459 IResource resource= fResources[i];
460 IContainer parent= resource.getParent();
461 if (!deletedChildren.contains(parent))
462 resources.add(resource);
465 fJavaElements= javaElements.toArray(new IJavaElement[javaElements.size()]);
466 fResources= resources.toArray(new IResource[resources.size()]);
470 * @param pack the package to delete
471 * @param packagesToDelete all packages to delete
472 * @return true if this initially selected package is really deletable
473 * (if it has non-selected subpackages, it may only be cleared).
474 * @throws JavaModelException should not happen
476 private boolean canRemoveCompletely(IPackageFragment pack, List<IPackageFragment> packagesToDelete) throws JavaModelException {
477 final IPackageFragment[] subPackages= JavaElementUtil.getPackageAndSubpackages(pack);
478 for (int i= 0; i < subPackages.length; i++) {
479 if (!subPackages[i].equals(pack) && !packagesToDelete.contains(subPackages[i]))
486 * Adds deletable parent packages of the fragment "frag" to the list
487 * "deletableParentPackages"; also adds the resources of those packages to the
488 * set "resourcesToDelete".
489 * @param frag the package fragment
490 * @param initialPackagesToDelete the initial packages to delete
491 * @param resourcesToDelete result to add resources to delete
492 * @param deletableParentPackages result ro add deletable parent packages
493 * @throws CoreException should not happen
495 private void addDeletableParentPackages(IPackageFragment frag, List<IPackageFragment> initialPackagesToDelete, Set<IResource> resourcesToDelete, List<IPackageFragment> deletableParentPackages)
496 throws CoreException {
498 if (frag.getResource().isLinked()) {
499 final IConfirmQuery query= fDeleteQueries.createYesNoQuery(RefactoringCoreMessages.JavaDeleteProcessor_confirm_linked_folder_delete, false, IReorgQueries.CONFIRM_DELETE_LINKED_PARENT);
500 if (!query.confirm(Messages.format(RefactoringCoreMessages.JavaDeleteProcessor_delete_linked_folder_question, BasicElementLabels.getResourceName(frag.getResource()))))
504 final IResource[] children= ((IContainer) frag.getResource()).members();
505 for (int i= 0; i < children.length; i++) {
506 // Child must be a package fragment already in the list,
507 // or a resource which is deleted as well.
508 if (!resourcesToDelete.contains(children[i]))
511 resourcesToDelete.add(frag.getResource());
512 deletableParentPackages.add(frag);
514 final IPackageFragment parent= JavaElementUtil.getParentSubpackage(frag);
515 if (parent != null && !initialPackagesToDelete.contains(parent))
516 addDeletableParentPackages(parent, initialPackagesToDelete, resourcesToDelete, deletableParentPackages);
519 // ask for confirmation of deletion of all package fragment roots that are
520 // on classpaths of other projects
521 private void removeUnconfirmedReferencedArchives() throws JavaModelException {
522 String queryTitle= RefactoringCoreMessages.DeleteRefactoring_2;
523 IConfirmQuery query= fDeleteQueries.createYesYesToAllNoNoToAllQuery(queryTitle, true, IReorgQueries.CONFIRM_DELETE_REFERENCED_ARCHIVES);
524 removeUnconfirmedReferencedPackageFragmentRoots(query);
525 removeUnconfirmedReferencedArchiveFiles(query);
528 private void removeUnconfirmedReferencedArchiveFiles(IConfirmQuery query) throws JavaModelException, OperationCanceledException {
529 List<IResource> filesToSkip= new ArrayList<IResource>(0);
530 for (int i= 0; i < fResources.length; i++) {
531 IResource resource= fResources[i];
532 if (! (resource instanceof IFile))
535 IJavaProject project= JavaCore.create(resource.getProject());
536 if (project == null || ! project.exists())
538 IPackageFragmentRoot root= project.findPackageFragmentRoot(resource.getFullPath());
541 List<IJavaProject> referencingProjects= Arrays.asList(JavaElementUtil.getReferencingProjects(root));
542 if (skipDeletingReferencedRoot(query, root, referencingProjects))
543 filesToSkip.add(resource);
545 removeFromSetToDelete(filesToSkip.toArray(new IFile[filesToSkip.size()]));
548 private void removeUnconfirmedReferencedPackageFragmentRoots(IConfirmQuery query) throws JavaModelException, OperationCanceledException {
549 List<IPackageFragmentRoot> rootsToSkip= new ArrayList<IPackageFragmentRoot>(0);
550 for (int i= 0; i < fJavaElements.length; i++) {
551 IJavaElement element= fJavaElements[i];
552 if (! (element instanceof IPackageFragmentRoot))
554 IPackageFragmentRoot root= (IPackageFragmentRoot)element;
555 ArrayList<IJavaProject> referencingProjects= new ArrayList<IJavaProject>(Arrays.asList(JavaElementUtil.getReferencingProjects(root)));
556 referencingProjects.remove(root.getJavaProject());
557 if (skipDeletingReferencedRoot(query, root, referencingProjects))
558 rootsToSkip.add(root);
560 removeFromSetToDelete(rootsToSkip.toArray(new IJavaElement[rootsToSkip.size()]));
563 private static boolean skipDeletingReferencedRoot(IConfirmQuery query, IPackageFragmentRoot root, List<IJavaProject> referencingProjects) throws OperationCanceledException {
564 if (referencingProjects.isEmpty() || root == null || ! root.exists() ||! root.isArchive())
566 String label= JavaElementLabels.getElementLabel(root, JavaElementLabels.ALL_DEFAULT);
567 String question= referencingProjects.size() == 1 ? Messages.format(RefactoringCoreMessages.DeleteRefactoring_3_singular, label) : Messages.format(
568 RefactoringCoreMessages.DeleteRefactoring_3_plural, label);
569 return ! query.confirm(question, referencingProjects.toArray());
572 private void removeUnconfirmedFoldersThatContainSourceFolders() throws CoreException {
573 String queryTitle= RefactoringCoreMessages.DeleteRefactoring_4;
574 IConfirmQuery query= fDeleteQueries.createYesYesToAllNoNoToAllQuery(queryTitle, true, IReorgQueries.CONFIRM_DELETE_FOLDERS_CONTAINING_SOURCE_FOLDERS);
575 List<IFolder> foldersToSkip= new ArrayList<IFolder>(0);
576 for (int i= 0; i < fResources.length; i++) {
577 IResource resource= fResources[i];
578 if (resource instanceof IFolder){
579 IFolder folder= (IFolder)resource;
580 if (containsSourceFolder(folder)){
581 String question= Messages.format(RefactoringCoreMessages.DeleteRefactoring_5, BasicElementLabels.getResourceName(folder));
582 if (! query.confirm(question))
583 foldersToSkip.add(folder);
587 removeFromSetToDelete(foldersToSkip.toArray(new IResource[foldersToSkip.size()]));
590 private static boolean containsSourceFolder(IFolder folder) throws CoreException {
591 IResource[] subFolders= folder.members();
592 for (int i = 0; i < subFolders.length; i++) {
593 if (! (subFolders[i] instanceof IFolder))
595 IJavaElement element= JavaCore.create(folder);
596 if (element instanceof IPackageFragmentRoot)
598 if (element instanceof IPackageFragment)
600 if (containsSourceFolder((IFolder)subFolders[i]))
606 private void removeElementsWithParentsInSelection() {
607 ParentChecker parentUtil= new ParentChecker(fResources, fJavaElements);
608 parentUtil.removeElementsWithAncestorsOnList(false);
609 fJavaElements= parentUtil.getJavaElements();
610 fResources= parentUtil.getResources();
613 private void removeJavaElementsChildrenOfJavaElements(){
614 ParentChecker parentUtil= new ParentChecker(fResources, fJavaElements);
615 parentUtil.removeElementsWithAncestorsOnList(true);
616 fJavaElements= parentUtil.getJavaElements();
620 public Change createChange(IProgressMonitor monitor) throws CoreException {
622 monitor.beginTask(RefactoringCoreMessages.JavaDeleteProcessor_creating_change, 1);
623 final Map<String, String> arguments= new HashMap<String, String>();
624 final String description= fElements.length == 1 ? RefactoringCoreMessages.JavaDeleteProcessor_description_singular : RefactoringCoreMessages.JavaDeleteProcessor_description_plural;
625 final IProject resource= getSingleProject();
626 final String project= resource != null ? resource.getName() : null;
627 final String source= project != null ? Messages.format(RefactoringCoreMessages.JavaDeleteProcessor_project_pattern, BasicElementLabels.getJavaElementName(project)) : RefactoringCoreMessages.JavaDeleteProcessor_workspace;
628 final String header= fElements.length == 1 ? Messages.format(RefactoringCoreMessages.JavaDeleteProcessor_header_singular, source) : Messages.format(
629 RefactoringCoreMessages.JavaDeleteProcessor_header_plural, new String[] { String.valueOf(fElements.length), source });
630 int flags= JavaRefactoringDescriptor.JAR_MIGRATION | JavaRefactoringDescriptor.JAR_REFACTORING | RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE;
631 final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header);
632 if (fDeleteSubPackages)
633 comment.addSetting(RefactoringCoreMessages.JavaDeleteProcessor_delete_subpackages);
634 if (fAccessorsDeleted)
635 comment.addSetting(RefactoringCoreMessages.JavaDeleteProcessor_delete_accessors);
636 final DeleteDescriptor descriptor= RefactoringSignatureDescriptorFactory.createDeleteDescriptor(project, description, comment.asString(), arguments, flags);
637 arguments.put(ATTRIBUTE_DELETE_SUBPACKAGES, Boolean.valueOf(fDeleteSubPackages).toString());
638 arguments.put(ATTRIBUTE_SUGGEST_ACCESSORS, Boolean.valueOf(fSuggestGetterSetterDeletion).toString());
639 arguments.put(ATTRIBUTE_RESOURCES, new Integer(fResources.length).toString());
640 for (int offset= 0; offset < fResources.length; offset++)
641 arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + (offset + 1), JavaRefactoringDescriptorUtil.resourceToHandle(project, fResources[offset]));
642 arguments.put(ATTRIBUTE_ELEMENTS, new Integer(fJavaElements.length).toString());
643 for (int offset= 0; offset < fJavaElements.length; offset++)
644 arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + (offset + fResources.length + 1), JavaRefactoringDescriptorUtil.elementToHandle(project, fJavaElements[offset]));
645 return new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.DeleteRefactoring_7, new Change[] { fDeleteChange});
651 private IProject getSingleProject() {
652 IProject first= null;
653 for (int index= 0; index < fElements.length; index++) {
654 IProject project= null;
655 if (fElements[index] instanceof IJavaElement)
656 project= ((IJavaElement) fElements[index]).getJavaProject().getProject();
657 else if (fElements[index] instanceof IResource)
658 project= ((IResource) fElements[index]).getProject();
659 if (project != null) {
662 else if (!project.equals(first))
669 private void addToSetToDelete(IJavaElement[] newElements){
670 fJavaElements= ReorgUtils.union(fJavaElements, newElements);
673 private void removeFromSetToDelete(IResource[] resourcesToNotDelete) {
674 fResources= ReorgUtils.setMinus(fResources, resourcesToNotDelete);
677 private void removeFromSetToDelete(IJavaElement[] elementsToNotDelete) {
678 fJavaElements= ReorgUtils.setMinus(fJavaElements, elementsToNotDelete);
681 private void addGettersSetters() throws JavaModelException {
682 IField[] fields= getFields(fJavaElements);
683 if (fields.length == 0)
685 //IField -> IMethod[]
686 Map<IField, IMethod[]> getterSetterMapping= createGetterSetterMapping(fields);
687 if (getterSetterMapping.isEmpty())
689 removeAlreadySelectedMethods(getterSetterMapping);
690 if (getterSetterMapping.isEmpty())
692 fAccessorsDeleted= true;
693 List<IMethod> gettersSettersToAdd= getGettersSettersToDelete(getterSetterMapping);
694 addToSetToDelete(gettersSettersToAdd.toArray(new IMethod[gettersSettersToAdd.size()]));
697 private List<IMethod> getGettersSettersToDelete(Map<IField, IMethod[]> getterSetterMapping) {
698 List<IMethod> gettersSettersToAdd= new ArrayList<IMethod>(getterSetterMapping.size());
699 String queryTitle= RefactoringCoreMessages.DeleteRefactoring_8;
700 IConfirmQuery getterSetterQuery= fDeleteQueries.createYesYesToAllNoNoToAllQuery(queryTitle, true, IReorgQueries.CONFIRM_DELETE_GETTER_SETTER);
701 for (Iterator<IField> iter= getterSetterMapping.keySet().iterator(); iter.hasNext();) {
702 IField field= iter.next();
703 Assert.isTrue(hasGetter(getterSetterMapping, field) || hasSetter(getterSetterMapping, field));
704 String deleteGetterSetter= Messages.format(RefactoringCoreMessages.DeleteRefactoring_9, JavaElementUtil.createFieldSignature(field));
705 if (getterSetterQuery.confirm(deleteGetterSetter)){
706 if (hasGetter(getterSetterMapping, field))
707 gettersSettersToAdd.add(getGetter(getterSetterMapping, field));
708 if (hasSetter(getterSetterMapping, field))
709 gettersSettersToAdd.add(getSetter(getterSetterMapping, field));
712 return gettersSettersToAdd;
715 //note: modifies the mapping
716 private void removeAlreadySelectedMethods(Map<IField, IMethod[]> getterSetterMapping) {
717 List<IJavaElement> elementsToDelete= Arrays.asList(fJavaElements);
718 for (Iterator<IField> iter= getterSetterMapping.keySet().iterator(); iter.hasNext();) {
719 IField field= iter.next();
721 IMethod getter= getGetter(getterSetterMapping, field);
722 if (getter != null && elementsToDelete.contains(getter))
723 removeGetterFromMapping(getterSetterMapping, field);
726 IMethod setter= getSetter(getterSetterMapping, field);
727 if (setter != null && elementsToDelete.contains(setter))
728 removeSetterFromMapping(getterSetterMapping, field);
730 //both getter and setter already included
731 if (! hasGetter(getterSetterMapping, field) && ! hasSetter(getterSetterMapping, field))
737 * IField -> IMethod[] (array of 2 - [getter, setter], one of which can be null)
739 private static Map<IField, IMethod[]> createGetterSetterMapping(IField[] fields) throws JavaModelException {
740 Map<IField, IMethod[]> result= new HashMap<IField, IMethod[]>();
741 for (int i= 0; i < fields.length; i++) {
742 IField field= fields[i];
743 IMethod[] getterSetter= getGetterSetter(field);
744 if (getterSetter != null)
745 result.put(field, getterSetter);
749 private static boolean hasSetter(Map<IField, IMethod[]> getterSetterMapping, IField field){
750 return getterSetterMapping.containsKey(field) &&
751 getSetter(getterSetterMapping, field) != null;
753 private static boolean hasGetter(Map<IField, IMethod[]> getterSetterMapping, IField field){
754 return getterSetterMapping.containsKey(field) &&
755 getGetter(getterSetterMapping, field) != null;
757 private static void removeGetterFromMapping(Map<IField, IMethod[]> getterSetterMapping, IField field){
758 getterSetterMapping.get(field)[0]= null;
760 private static void removeSetterFromMapping(Map<IField, IMethod[]> getterSetterMapping, IField field){
761 getterSetterMapping.get(field)[1]= null;
763 private static IMethod getGetter(Map<IField, IMethod[]> getterSetterMapping, IField field){
764 return getterSetterMapping.get(field)[0];
766 private static IMethod getSetter(Map<IField, IMethod[]> getterSetterMapping, IField field){
767 return getterSetterMapping.get(field)[1];
769 private static IField[] getFields(IJavaElement[] elements){
770 List<IJavaElement> fields= new ArrayList<IJavaElement>(3);
771 for (int i= 0; i < elements.length; i++) {
772 if (elements[i] instanceof IField)
773 fields.add(elements[i]);
775 return fields.toArray(new IField[fields.size()]);
779 * returns an array of 2 [getter, setter] or null if no getter or setter exists
781 private static IMethod[] getGetterSetter(IField field) throws JavaModelException {
782 IMethod getter= GetterSetterUtil.getGetter(field);
783 IMethod setter= GetterSetterUtil.getSetter(field);
784 if (getter != null && getter.exists() || setter != null && setter.exists())
785 return new IMethod[]{getter, setter};
790 //----------- read-only confirmation business ------
791 private void confirmDeletingReadOnly() throws CoreException {
792 if (! ReadOnlyResourceFinder.confirmDeleteOfReadOnlyElements(fJavaElements, fResources, fDeleteQueries))
793 throw new OperationCanceledException(); //saying 'no' to this one is like cancelling the whole operation
796 //----------- empty CUs related method
797 private void addEmptyCusToDelete() throws JavaModelException {
798 Set<ICompilationUnit> cusToEmpty= getCusToEmpty();
799 addToSetToDelete(cusToEmpty.toArray(new ICompilationUnit[cusToEmpty.size()]));
802 private Set<ICompilationUnit> getCusToEmpty() throws JavaModelException {
803 Set<ICompilationUnit> result= new HashSet<ICompilationUnit>();
804 for (int i= 0; i < fJavaElements.length; i++) {
805 IJavaElement element= fJavaElements[i];
806 ICompilationUnit cu= ReorgUtils.getCompilationUnit(element);
807 if (cu != null && ! result.contains(cu) && willHaveAllTopLevelTypesDeleted(cu))
813 private boolean willHaveAllTopLevelTypesDeleted(ICompilationUnit cu) throws JavaModelException {
814 Set<IJavaElement> elementSet= new HashSet<IJavaElement>(Arrays.asList(fJavaElements));
815 IType[] topLevelTypes= cu.getTypes();
816 for (int i= 0; i < topLevelTypes.length; i++) {
817 if (! elementSet.contains(topLevelTypes[i]))
823 private RefactoringStatus initialize(JavaRefactoringArguments extended) {
824 setQueries(new NullReorgQueries());
825 final RefactoringStatus status= new RefactoringStatus();
826 final String subPackages= extended.getAttribute(ATTRIBUTE_DELETE_SUBPACKAGES);
827 if (subPackages != null) {
828 fDeleteSubPackages= Boolean.valueOf(subPackages).booleanValue();
830 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_DELETE_SUBPACKAGES));
831 final String suggest= extended.getAttribute(ATTRIBUTE_SUGGEST_ACCESSORS);
832 if (suggest != null) {
833 fSuggestGetterSetterDeletion= Boolean.valueOf(suggest).booleanValue();
835 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_SUGGEST_ACCESSORS));
836 int resourceCount= 0;
838 String value= extended.getAttribute(ATTRIBUTE_RESOURCES);
839 if (value != null && !"".equals(value)) {//$NON-NLS-1$
841 resourceCount= Integer.parseInt(value);
842 } catch (NumberFormatException exception) {
843 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_RESOURCES));
846 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_RESOURCES));
847 value= extended.getAttribute(ATTRIBUTE_ELEMENTS);
848 if (value != null && !"".equals(value)) {//$NON-NLS-1$
850 elementCount= Integer.parseInt(value);
851 } catch (NumberFormatException exception) {
852 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_ELEMENTS));
855 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_ELEMENTS));
857 List<IAdaptable> elements= new ArrayList<IAdaptable>();
858 for (int index= 0; index < resourceCount; index++) {
859 final String attribute= JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + (index + 1);
860 handle= extended.getAttribute(attribute);
861 if (handle != null && !"".equals(handle)) { //$NON-NLS-1$
862 final IResource resource= JavaRefactoringDescriptorUtil.handleToResource(extended.getProject(), handle);
863 if (resource == null || !resource.exists())
864 status.merge(JavaRefactoringDescriptorUtil.createInputWarningStatus(resource, getProcessorName(), IJavaRefactorings.DELETE));
866 elements.add(resource);
868 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, attribute));
870 fResources= elements.toArray(new IResource[elements.size()]);
871 elements= new ArrayList<IAdaptable>();
872 for (int index= 0; index < elementCount; index++) {
873 final String attribute= JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + (resourceCount + index + 1);
874 handle= extended.getAttribute(attribute);
875 if (handle != null && !"".equals(handle)) { //$NON-NLS-1$
876 final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(extended.getProject(), handle, false);
877 if (element == null || !element.exists())
878 status.merge(JavaRefactoringDescriptorUtil.createInputWarningStatus(element, getProcessorName(), IJavaRefactorings.DELETE));
880 elements.add(element);
882 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, attribute));
884 fJavaElements= elements.toArray(new IJavaElement[elements.size()]);
885 fElements= new Object[fResources.length + fJavaElements.length];
886 System.arraycopy(fResources, 0, fElements, 0, fResources.length);
887 System.arraycopy(fJavaElements, 0, fElements, fResources.length, fJavaElements.length);