]> git.uio.no Git - ifi-stolz-refaktor.git/blob - case-study/jdt-before/core refactoring/org/eclipse/jdt/internal/corext/refactoring/reorg/JavaDeleteProcessor.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-before / core refactoring / org / eclipse / jdt / internal / corext / refactoring / reorg / JavaDeleteProcessor.java
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
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.jdt.internal.corext.refactoring.reorg;
12
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;
21 import java.util.Map;
22 import java.util.Set;
23
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;
29
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;
37
38 import org.eclipse.core.filebuffers.FileBuffers;
39 import org.eclipse.core.filebuffers.ITextFileBuffer;
40 import org.eclipse.core.filebuffers.LocationKind;
41
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;
50
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;
64
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;
80
81 import org.eclipse.jdt.ui.JavaElementLabels;
82 import org.eclipse.jdt.ui.refactoring.IRefactoringProcessorIds;
83
84 import org.eclipse.jdt.internal.ui.JavaPlugin;
85 import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
86
87 public final class JavaDeleteProcessor extends DeleteProcessor {
88
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$
93
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;
102
103         private Change fDeleteChange;
104         private boolean fDeleteSubPackages;
105
106         public JavaDeleteProcessor(Object[] elements) {
107                 fElements= elements;
108                 if (fElements != null) {
109                         fResources= RefactoringAvailabilityTester.getResources(elements);
110                         fJavaElements= RefactoringAvailabilityTester.getJavaElements(elements);
111                 }
112                 fSuggestGetterSetterDeletion= true;
113                 fDeleteSubPackages= false;
114                 fWasCanceled= false;
115         }
116
117         public JavaDeleteProcessor(JavaRefactoringArguments arguments, RefactoringStatus status) {
118                 this(null);
119                 RefactoringStatus initStatus= initialize(arguments);
120                 status.merge(initStatus);
121         }
122
123         @Override
124         public String getIdentifier() {
125                 return IRefactoringProcessorIds.DELETE_PROCESSOR;
126         }
127
128         @Override
129         public boolean isApplicable() throws CoreException {
130                 if (fElements.length == 0)
131                         return false;
132                 if (fElements.length != fResources.length + fJavaElements.length)
133                         return false;
134                 for (int i= 0; i < fResources.length; i++) {
135                         if (!RefactoringAvailabilityTester.isDeleteAvailable(fResources[i]))
136                                 return false;
137                 }
138                 for (int i= 0; i < fJavaElements.length; i++) {
139                         if (!RefactoringAvailabilityTester.isDeleteAvailable(fJavaElements[i]))
140                                 return false;
141                 }
142                 return true;
143         }
144
145         public boolean needsProgressMonitor() {
146                 if (fResources != null && fResources.length > 0)
147                         return true;
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)
152                                         return true;
153                         }
154                 }
155                 return false;
156
157         }
158
159         @Override
160         public String getProcessorName() {
161                 return RefactoringCoreMessages.DeleteRefactoring_7;
162         }
163
164         @Override
165         public Object[] getElements() {
166                 return fElements;
167         }
168
169         @Override
170         public RefactoringParticipant[] loadParticipants(RefactoringStatus status, SharableParticipants shared) throws CoreException {
171                 return fDeleteModifications.loadParticipants(status, this, getAffectedProjectNatures(), shared);
172         }
173
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()]);
181         }
182
183         /*
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.
186          */
187         public void setSuggestGetterSetterDeletion(boolean suggest){
188                 fSuggestGetterSetterDeletion= suggest;
189         }
190
191         public void setDeleteSubPackages(boolean selection) {
192                 fDeleteSubPackages= selection;
193         }
194
195         public boolean getDeleteSubPackages() {
196                 return fDeleteSubPackages;
197         }
198
199         public boolean hasSubPackagesToDelete() {
200                 try {
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())
207                                                 return true;
208                                 }
209                         }
210                 } catch (JavaModelException e) {
211                         JavaPlugin.log(e);
212                 }
213                 return false;
214         }
215
216         public void setQueries(IReorgQueries queries){
217                 Assert.isNotNull(queries);
218                 fDeleteQueries= queries;
219         }
220
221         public IJavaElement[] getJavaElementsToDelete(){
222                 return fJavaElements;
223         }
224
225         public boolean wasCanceled() {
226                 return fWasCanceled;
227         }
228
229         public IResource[] getResourcesToDelete(){
230                 return fResources;
231         }
232
233         /* (non-Javadoc)
234          * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkActivation(org.eclipse.core.runtime.IProgressMonitor)
235          */
236         @Override
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.");
248                         }
249                 }
250                 return result;
251         }
252
253         /* (non-Javadoc)
254          * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkInput(org.eclipse.core.runtime.IProgressMonitor)
255          */
256         @Override
257         public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditionsContext context) throws CoreException {
258                 pm.beginTask(RefactoringCoreMessages.DeleteRefactoring_1, 1);
259                 try{
260                         fWasCanceled= false;
261                         RefactoringStatus result= new RefactoringStatus();
262
263                         recalculateElementsToDelete();
264
265                         checkDirtyCompilationUnits(result);
266                         checkDirtyResources(result);
267                         fDeleteModifications= new DeleteModifications();
268                         fDeleteModifications.delete(fResources);
269                         fDeleteModifications.delete(fJavaElements);
270                         List<IResource> packageDeletes= fDeleteModifications.postProcess();
271
272                         TextChangeManager manager= new TextChangeManager();
273                         fDeleteChange= DeleteChangeCreator.createDeleteChange(manager, fResources, fJavaElements, getProcessorName(), packageDeletes);
274
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]);
281                         }
282                         return result;
283                 } catch (OperationCanceledException e) {
284                         fWasCanceled= true;
285                         throw e;
286                 } finally{
287                         pm.done();
288                 }
289         }
290
291         private void checkDirtyCompilationUnits(RefactoringStatus result) throws CoreException {
292                 if (fJavaElements == null || fJavaElements.length == 0)
293                         return;
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]);
302                                 }
303                         }
304                 }
305         }
306
307         private void checkDirtyCompilationUnit(RefactoringStatus result, ICompilationUnit cunit) {
308                 IResource resource= cunit.getResource();
309                 if (resource == null || resource.getType() != IResource.FILE)
310                         return;
311                 checkDirtyFile(result, (IFile)resource);
312         }
313
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);
321                                         }
322                                         return true;
323                                 }
324                         }, IResource.DEPTH_INFINITE, false);
325                 }
326         }
327
328         private void checkDirtyFile(RefactoringStatus result, IFile file) {
329                 if (file == null || !file.exists())
330                         return;
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)));
337                         } else {
338                                 result.addFatalError(Messages.format(
339                                         RefactoringCoreMessages.JavaDeleteProcessor_unsaved_changes,
340                                         BasicElementLabels.getPathLabel(file.getFullPath(), false)));
341                         }
342                 }
343         }
344
345         /*
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,
349          * it asks the user.
350          */
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. */
355                         addSubPackages();
356
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*/
366
367                 if (fSuggestGetterSetterDeletion)
368                         addGettersSetters();/*at the end - this cannot invalidate anything*/
369
370                 addDeletableParentPackagesOnPackageDeletion(); /* do not change the sequence in fJavaElements after this method */
371         }
372
373         /**
374          * Adds all subpackages of the selected packages to the list of items to be
375          * deleted.
376          *
377          * @throws JavaModelException should not happen
378          */
379         private void addSubPackages() throws JavaModelException {
380
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])));
385                         } else {
386                                 javaElements.add(fJavaElements[i]);
387                         }
388                 }
389
390                 fJavaElements= javaElements.toArray(new IJavaElement[javaElements.size()]);
391         }
392
393         /**
394          * Add deletable parent packages to the list of items to delete.
395          *
396          * @throws CoreException should not happen
397          */
398         private void addDeletableParentPackagesOnPackageDeletion() throws CoreException {
399
400                 final List<IPackageFragment> initialPackagesToDelete= (List<IPackageFragment>) ReorgUtils.getElementsOfType(fJavaElements, IJavaElement.PACKAGE_FRAGMENT);
401
402                 if (initialPackagesToDelete.size() == 0)
403                         return;
404
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());
409                         }
410                 });
411
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());
418                 }
419
420                 // new package list in the right sequence
421                 final List<IPackageFragment>allFragmentsToDelete= new ArrayList<IPackageFragment>();
422
423                 for (Iterator<IPackageFragment> outerIter= initialPackagesToDelete.iterator(); outerIter.hasNext();) {
424                         final IPackageFragment currentPackageFragment= outerIter.next();
425
426                         // The package will at least be cleared
427                         allFragmentsToDelete.add(currentPackageFragment);
428
429                         if (canRemoveCompletely(currentPackageFragment, initialPackagesToDelete)) {
430
431                                 final IPackageFragment parent= JavaElementUtil.getParentSubpackage(currentPackageFragment);
432                                 if (parent != null && !initialPackagesToDelete.contains(parent)) {
433
434                                         final List<IPackageFragment>emptyParents= new ArrayList<IPackageFragment>();
435                                         addDeletableParentPackages(parent, initialPackagesToDelete, deletedChildren, emptyParents);
436
437                                         // Add parents in the right sequence (inner to outer)
438                                         allFragmentsToDelete.addAll(emptyParents);
439                                 }
440                         }
441                 }
442
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]);
451                         }
452                 }
453                 // Re-add deleted packages - note the (new) sequence
454                 javaElements.addAll(allFragmentsToDelete);
455
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);
463                 }
464
465                 fJavaElements= javaElements.toArray(new IJavaElement[javaElements.size()]);
466                 fResources= resources.toArray(new IResource[resources.size()]);
467         }
468
469         /**
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
475          */
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]))
480                                 return false;
481                 }
482                 return true;
483         }
484
485         /**
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
494          */
495         private void addDeletableParentPackages(IPackageFragment frag, List<IPackageFragment> initialPackagesToDelete, Set<IResource> resourcesToDelete, List<IPackageFragment> deletableParentPackages)
496                         throws CoreException {
497
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()))))
501                                         return;
502                 }
503
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]))
509                                 return;
510                 }
511                 resourcesToDelete.add(frag.getResource());
512                 deletableParentPackages.add(frag);
513
514                 final IPackageFragment parent= JavaElementUtil.getParentSubpackage(frag);
515                 if (parent != null && !initialPackagesToDelete.contains(parent))
516                         addDeletableParentPackages(parent, initialPackagesToDelete, resourcesToDelete, deletableParentPackages);
517         }
518
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);
526         }
527
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))
533                                 continue;
534
535                         IJavaProject project= JavaCore.create(resource.getProject());
536                         if (project == null || ! project.exists())
537                                 continue;
538                         IPackageFragmentRoot root= project.findPackageFragmentRoot(resource.getFullPath());
539                         if (root == null)
540                                 continue;
541                         List<IJavaProject> referencingProjects= Arrays.asList(JavaElementUtil.getReferencingProjects(root));
542                         if (skipDeletingReferencedRoot(query, root, referencingProjects))
543                                 filesToSkip.add(resource);
544                 }
545                 removeFromSetToDelete(filesToSkip.toArray(new IFile[filesToSkip.size()]));
546         }
547
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))
553                                 continue;
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);
559                 }
560                 removeFromSetToDelete(rootsToSkip.toArray(new IJavaElement[rootsToSkip.size()]));
561         }
562
563         private static boolean skipDeletingReferencedRoot(IConfirmQuery query, IPackageFragmentRoot root, List<IJavaProject> referencingProjects) throws OperationCanceledException {
564                 if (referencingProjects.isEmpty() || root == null || ! root.exists() ||! root.isArchive())
565                         return false;
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());
570         }
571
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);
584                                 }
585                         }
586                 }
587                 removeFromSetToDelete(foldersToSkip.toArray(new IResource[foldersToSkip.size()]));
588         }
589
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))
594                                 continue;
595                         IJavaElement element= JavaCore.create(folder);
596                         if (element instanceof IPackageFragmentRoot)
597                                 return true;
598                         if (element instanceof IPackageFragment)
599                                 continue;
600                         if (containsSourceFolder((IFolder)subFolders[i]))
601                                 return true;
602                 }
603                 return false;
604         }
605
606         private void removeElementsWithParentsInSelection() {
607                 ParentChecker parentUtil= new ParentChecker(fResources, fJavaElements);
608                 parentUtil.removeElementsWithAncestorsOnList(false);
609                 fJavaElements= parentUtil.getJavaElements();
610                 fResources= parentUtil.getResources();
611         }
612
613         private void removeJavaElementsChildrenOfJavaElements(){
614                 ParentChecker parentUtil= new ParentChecker(fResources, fJavaElements);
615                 parentUtil.removeElementsWithAncestorsOnList(true);
616                 fJavaElements= parentUtil.getJavaElements();
617         }
618
619         @Override
620         public Change createChange(IProgressMonitor monitor) throws CoreException {
621                 try {
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});
646                 } finally {
647                         monitor.done();
648                 }
649         }
650
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) {
660                                 if (first == null)
661                                         first= project;
662                                 else if (!project.equals(first))
663                                         return null;
664                         }
665                 }
666                 return first;
667         }
668
669         private void addToSetToDelete(IJavaElement[] newElements){
670                 fJavaElements= ReorgUtils.union(fJavaElements, newElements);
671         }
672
673         private void removeFromSetToDelete(IResource[] resourcesToNotDelete) {
674                 fResources= ReorgUtils.setMinus(fResources, resourcesToNotDelete);
675         }
676
677         private void removeFromSetToDelete(IJavaElement[] elementsToNotDelete) {
678                 fJavaElements= ReorgUtils.setMinus(fJavaElements, elementsToNotDelete);
679         }
680
681         private void addGettersSetters() throws JavaModelException {
682                 IField[] fields= getFields(fJavaElements);
683                 if (fields.length == 0)
684                         return;
685                 //IField -> IMethod[]
686                 Map<IField, IMethod[]> getterSetterMapping= createGetterSetterMapping(fields);
687                 if (getterSetterMapping.isEmpty())
688                         return;
689                 removeAlreadySelectedMethods(getterSetterMapping);
690                 if (getterSetterMapping.isEmpty())
691                         return;
692                 fAccessorsDeleted= true;
693                 List<IMethod> gettersSettersToAdd= getGettersSettersToDelete(getterSetterMapping);
694                 addToSetToDelete(gettersSettersToAdd.toArray(new IMethod[gettersSettersToAdd.size()]));
695         }
696
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));
710                         }
711                 }
712                 return gettersSettersToAdd;
713         }
714
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();
720                         //remove getter
721                         IMethod getter= getGetter(getterSetterMapping, field);
722                         if (getter != null && elementsToDelete.contains(getter))
723                                 removeGetterFromMapping(getterSetterMapping, field);
724
725                         //remove setter
726                         IMethod setter= getSetter(getterSetterMapping, field);
727                         if (setter != null && elementsToDelete.contains(setter))
728                                 removeSetterFromMapping(getterSetterMapping, field);
729
730                         //both getter and setter already included
731                         if (! hasGetter(getterSetterMapping, field) && ! hasSetter(getterSetterMapping, field))
732                                 iter.remove();
733                 }
734         }
735
736         /*
737          * IField -> IMethod[] (array of 2 - [getter, setter], one of which can be null)
738          */
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);
746                 }
747                 return result;
748         }
749         private static boolean hasSetter(Map<IField, IMethod[]> getterSetterMapping, IField field){
750                 return getterSetterMapping.containsKey(field) &&
751                            getSetter(getterSetterMapping, field) != null;
752         }
753         private static boolean hasGetter(Map<IField, IMethod[]> getterSetterMapping, IField field){
754                 return getterSetterMapping.containsKey(field) &&
755                            getGetter(getterSetterMapping, field) != null;
756         }
757         private static void removeGetterFromMapping(Map<IField, IMethod[]> getterSetterMapping, IField field){
758                 getterSetterMapping.get(field)[0]= null;
759         }
760         private static void removeSetterFromMapping(Map<IField, IMethod[]> getterSetterMapping, IField field){
761                 getterSetterMapping.get(field)[1]= null;
762         }
763         private static IMethod getGetter(Map<IField, IMethod[]> getterSetterMapping, IField field){
764                 return getterSetterMapping.get(field)[0];
765         }
766         private static IMethod getSetter(Map<IField, IMethod[]> getterSetterMapping, IField field){
767                 return getterSetterMapping.get(field)[1];
768         }
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]);
774                 }
775                 return fields.toArray(new IField[fields.size()]);
776         }
777
778         /*
779          * returns an array of 2 [getter, setter] or null if no getter or setter exists
780          */
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};
786                 else
787                         return null;
788         }
789
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
794         }
795
796         //----------- empty CUs related method
797         private void addEmptyCusToDelete() throws JavaModelException {
798                 Set<ICompilationUnit> cusToEmpty= getCusToEmpty();
799                 addToSetToDelete(cusToEmpty.toArray(new ICompilationUnit[cusToEmpty.size()]));
800         }
801
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))
808                                 result.add(cu);
809                 }
810                 return result;
811         }
812
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]))
818                                 return false;
819                 }
820                 return true;
821         }
822
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();
829                 } else
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();
834                 } else
835                         return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_SUGGEST_ACCESSORS));
836                 int resourceCount= 0;
837                 int elementCount= 0;
838                 String value= extended.getAttribute(ATTRIBUTE_RESOURCES);
839                 if (value != null && !"".equals(value)) {//$NON-NLS-1$
840                         try {
841                                 resourceCount= Integer.parseInt(value);
842                         } catch (NumberFormatException exception) {
843                                 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_RESOURCES));
844                         }
845                 } else
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$
849                         try {
850                                 elementCount= Integer.parseInt(value);
851                         } catch (NumberFormatException exception) {
852                                 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_ELEMENTS));
853                         }
854                 } else
855                         return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_ELEMENTS));
856                 String handle= null;
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));
865                                 else
866                                         elements.add(resource);
867                         } else
868                                 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, attribute));
869                 }
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));
879                                 else
880                                         elements.add(element);
881                         } else
882                                 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, attribute));
883                 }
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);
888                 return status;
889         }
890 }