0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.jdt.internal.corext.refactoring.reorg;
0011:
0012: import java.util.ArrayList;
0013: import java.util.Arrays;
0014: import java.util.Collections;
0015: import java.util.Comparator;
0016: import java.util.HashMap;
0017: import java.util.HashSet;
0018: import java.util.Iterator;
0019: import java.util.List;
0020: import java.util.Map;
0021: import java.util.Set;
0022:
0023: import org.eclipse.core.runtime.Assert;
0024: import org.eclipse.core.runtime.CoreException;
0025: import org.eclipse.core.runtime.IProgressMonitor;
0026: import org.eclipse.core.runtime.OperationCanceledException;
0027:
0028: import org.eclipse.core.filebuffers.FileBuffers;
0029: import org.eclipse.core.filebuffers.ITextFileBuffer;
0030: import org.eclipse.core.filebuffers.LocationKind;
0031:
0032: import org.eclipse.core.resources.IContainer;
0033: import org.eclipse.core.resources.IFile;
0034: import org.eclipse.core.resources.IFolder;
0035: import org.eclipse.core.resources.IProject;
0036: import org.eclipse.core.resources.IResource;
0037: import org.eclipse.core.resources.IResourceVisitor;
0038: import org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory;
0039:
0040: import org.eclipse.ltk.core.refactoring.Change;
0041: import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
0042: import org.eclipse.ltk.core.refactoring.RefactoringStatus;
0043: import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
0044: import org.eclipse.ltk.core.refactoring.participants.DeleteProcessor;
0045: import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;
0046: import org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant;
0047: import org.eclipse.ltk.core.refactoring.participants.ResourceChangeChecker;
0048: import org.eclipse.ltk.core.refactoring.participants.SharableParticipants;
0049:
0050: import org.eclipse.jdt.core.ICompilationUnit;
0051: import org.eclipse.jdt.core.IField;
0052: import org.eclipse.jdt.core.IJavaElement;
0053: import org.eclipse.jdt.core.IJavaProject;
0054: import org.eclipse.jdt.core.IMethod;
0055: import org.eclipse.jdt.core.IPackageFragment;
0056: import org.eclipse.jdt.core.IPackageFragmentRoot;
0057: import org.eclipse.jdt.core.IType;
0058: import org.eclipse.jdt.core.JavaCore;
0059: import org.eclipse.jdt.core.JavaModelException;
0060: import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
0061: import org.eclipse.jdt.core.refactoring.descriptors.DeleteDescriptor;
0062: import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
0063:
0064: import org.eclipse.jdt.internal.corext.codemanipulation.GetterSetterUtil;
0065: import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
0066: import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
0067: import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
0068: import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester;
0069: import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
0070: import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
0071: import org.eclipse.jdt.internal.corext.refactoring.code.ScriptableRefactoring;
0072: import org.eclipse.jdt.internal.corext.refactoring.participants.JavaProcessors;
0073: import org.eclipse.jdt.internal.corext.refactoring.participants.ResourceProcessors;
0074: import org.eclipse.jdt.internal.corext.refactoring.tagging.ICommentProvider;
0075: import org.eclipse.jdt.internal.corext.refactoring.tagging.IScriptableRefactoring;
0076: import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil;
0077: import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
0078: import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager;
0079: import org.eclipse.jdt.internal.corext.util.Messages;
0080: import org.eclipse.jdt.internal.corext.util.Resources;
0081:
0082: import org.eclipse.jdt.ui.refactoring.IRefactoringProcessorIds;
0083:
0084: import org.eclipse.jdt.internal.ui.JavaPlugin;
0085:
0086: public final class JavaDeleteProcessor extends DeleteProcessor
0087: implements IScriptableRefactoring, ICommentProvider {
0088:
0089: private static final String ATTRIBUTE_RESOURCES = "resources"; //$NON-NLS-1$
0090: private static final String ATTRIBUTE_ELEMENTS = "elements"; //$NON-NLS-1$
0091: private static final String ATTRIBUTE_SUGGEST_ACCESSORS = "accessors"; //$NON-NLS-1$
0092: private static final String ATTRIBUTE_DELETE_SUBPACKAGES = "subPackages"; //$NON-NLS-1$
0093:
0094: private boolean fAccessorsDeleted;
0095: private boolean fWasCanceled;
0096: private boolean fSuggestGetterSetterDeletion;
0097: private Object[] fElements;
0098: private IResource[] fResources;
0099: private IJavaElement[] fJavaElements;
0100: private IReorgQueries fDeleteQueries;
0101: private DeleteModifications fDeleteModifications;
0102: private String fComment;
0103:
0104: private Change fDeleteChange;
0105: private boolean fDeleteSubPackages;
0106:
0107: public JavaDeleteProcessor(Object[] elements) {
0108: fElements = elements;
0109: if (fElements != null) {
0110: fResources = RefactoringAvailabilityTester
0111: .getResources(elements);
0112: fJavaElements = RefactoringAvailabilityTester
0113: .getJavaElements(elements);
0114: }
0115: fSuggestGetterSetterDeletion = true;
0116: fDeleteSubPackages = false;
0117: fWasCanceled = false;
0118: }
0119:
0120: public String getIdentifier() {
0121: return IRefactoringProcessorIds.DELETE_PROCESSOR;
0122: }
0123:
0124: public boolean isApplicable() throws CoreException {
0125: if (fElements.length == 0)
0126: return false;
0127: if (fElements.length != fResources.length
0128: + fJavaElements.length)
0129: return false;
0130: for (int i = 0; i < fResources.length; i++) {
0131: if (!RefactoringAvailabilityTester
0132: .isDeleteAvailable(fResources[i]))
0133: return false;
0134: }
0135: for (int i = 0; i < fJavaElements.length; i++) {
0136: if (!RefactoringAvailabilityTester
0137: .isDeleteAvailable(fJavaElements[i]))
0138: return false;
0139: }
0140: return true;
0141: }
0142:
0143: public boolean needsProgressMonitor() {
0144: if (fResources != null && fResources.length > 0)
0145: return true;
0146: if (fJavaElements != null) {
0147: for (int i = 0; i < fJavaElements.length; i++) {
0148: int type = fJavaElements[i].getElementType();
0149: if (type <= IJavaElement.CLASS_FILE)
0150: return true;
0151: }
0152: }
0153: return false;
0154:
0155: }
0156:
0157: public String getProcessorName() {
0158: return RefactoringCoreMessages.DeleteRefactoring_7;
0159: }
0160:
0161: public Object[] getElements() {
0162: return fElements;
0163: }
0164:
0165: public RefactoringParticipant[] loadParticipants(
0166: RefactoringStatus status, SharableParticipants shared)
0167: throws CoreException {
0168: return fDeleteModifications.loadParticipants(status, this ,
0169: getAffectedProjectNatures(), shared);
0170: }
0171:
0172: private String[] getAffectedProjectNatures() throws CoreException {
0173: String[] jNatures = JavaProcessors
0174: .computeAffectedNaturs(fJavaElements);
0175: String[] rNatures = ResourceProcessors
0176: .computeAffectedNatures(fResources);
0177: Set result = new HashSet();
0178: result.addAll(Arrays.asList(jNatures));
0179: result.addAll(Arrays.asList(rNatures));
0180: return (String[]) result.toArray(new String[result.size()]);
0181: }
0182:
0183: /*
0184: * This has to be customizable because when drag and drop is performed on a field,
0185: * you don't want to suggest deleting getter/setter if only the field was moved.
0186: */
0187: public void setSuggestGetterSetterDeletion(boolean suggest) {
0188: fSuggestGetterSetterDeletion = suggest;
0189: }
0190:
0191: public void setDeleteSubPackages(boolean selection) {
0192: fDeleteSubPackages = selection;
0193: }
0194:
0195: public boolean getDeleteSubPackages() {
0196: return fDeleteSubPackages;
0197: }
0198:
0199: public boolean hasSubPackagesToDelete() {
0200: try {
0201: for (int i = 0; i < fJavaElements.length; i++) {
0202: if (fJavaElements[i] instanceof IPackageFragment) {
0203: IPackageFragment packageFragment = (IPackageFragment) fJavaElements[i];
0204: if (packageFragment.isDefaultPackage())
0205: continue; // see bug 132576 (can remove this if(..) continue; statement when bug is fixed)
0206: if (packageFragment.hasSubpackages())
0207: return true;
0208: }
0209: }
0210: } catch (JavaModelException e) {
0211: JavaPlugin.log(e);
0212: }
0213: return false;
0214: }
0215:
0216: public void setQueries(IReorgQueries queries) {
0217: Assert.isNotNull(queries);
0218: fDeleteQueries = queries;
0219: }
0220:
0221: public IJavaElement[] getJavaElementsToDelete() {
0222: return fJavaElements;
0223: }
0224:
0225: public boolean wasCanceled() {
0226: return fWasCanceled;
0227: }
0228:
0229: public IResource[] getResourcesToDelete() {
0230: return fResources;
0231: }
0232:
0233: /* (non-Javadoc)
0234: * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkActivation(org.eclipse.core.runtime.IProgressMonitor)
0235: */
0236: public RefactoringStatus checkInitialConditions(IProgressMonitor pm)
0237: throws CoreException {
0238: Assert.isNotNull(fDeleteQueries);//must be set before checking activation
0239: RefactoringStatus result = new RefactoringStatus();
0240: result.merge(RefactoringStatus.create(Resources
0241: .checkInSync(ReorgUtils.getNotLinked(fResources))));
0242: IResource[] javaResources = ReorgUtils
0243: .getResources(fJavaElements);
0244: result.merge(RefactoringStatus.create(Resources
0245: .checkInSync(ReorgUtils.getNotNulls(javaResources))));
0246: for (int i = 0; i < fJavaElements.length; i++) {
0247: IJavaElement element = fJavaElements[i];
0248: if (element instanceof IType
0249: && ((IType) element).isAnonymous()) {
0250: // work around for bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=44450
0251: // result.addFatalError("Currently, there isn't any support to delete an anonymous type.");
0252: }
0253: }
0254: return result;
0255: }
0256:
0257: /* (non-Javadoc)
0258: * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkInput(org.eclipse.core.runtime.IProgressMonitor)
0259: */
0260: public RefactoringStatus checkFinalConditions(IProgressMonitor pm,
0261: CheckConditionsContext context) throws CoreException {
0262: pm.beginTask(RefactoringCoreMessages.DeleteRefactoring_1, 1);
0263: try {
0264: fWasCanceled = false;
0265: RefactoringStatus result = new RefactoringStatus();
0266:
0267: recalculateElementsToDelete();
0268:
0269: checkDirtyCompilationUnits(result);
0270: checkDirtyResources(result);
0271: fDeleteModifications = new DeleteModifications();
0272: fDeleteModifications.delete(fResources);
0273: fDeleteModifications.delete(fJavaElements);
0274: List/*<IResource>*/packageDeletes = fDeleteModifications
0275: .postProcess();
0276:
0277: TextChangeManager manager = new TextChangeManager();
0278: fDeleteChange = DeleteChangeCreator.createDeleteChange(
0279: manager, fResources, fJavaElements,
0280: getProcessorName(), packageDeletes);
0281:
0282: ResourceChangeChecker checker = (ResourceChangeChecker) context
0283: .getChecker(ResourceChangeChecker.class);
0284: IResourceChangeDescriptionFactory deltaFactory = checker
0285: .getDeltaFactory();
0286: fDeleteModifications.buildDelta(deltaFactory);
0287: IFile[] files = ResourceUtil.getFiles(manager
0288: .getAllCompilationUnits());
0289: for (int i = 0; i < files.length; i++) {
0290: deltaFactory.change(files[i]);
0291: }
0292: return result;
0293: } catch (OperationCanceledException e) {
0294: fWasCanceled = true;
0295: throw e;
0296: } catch (JavaModelException e) {
0297: throw e;
0298: } catch (CoreException e) {
0299: throw new JavaModelException(e);
0300: } finally {
0301: pm.done();
0302: }
0303: }
0304:
0305: private void checkDirtyCompilationUnits(RefactoringStatus result)
0306: throws CoreException {
0307: if (fJavaElements == null || fJavaElements.length == 0)
0308: return;
0309: for (int je = 0; je < fJavaElements.length; je++) {
0310: IJavaElement element = fJavaElements[je];
0311: if (element instanceof ICompilationUnit) {
0312: checkDirtyCompilationUnit(result,
0313: (ICompilationUnit) element);
0314: } else if (element instanceof IPackageFragment) {
0315: ICompilationUnit[] units = ((IPackageFragment) element)
0316: .getCompilationUnits();
0317: for (int u = 0; u < units.length; u++) {
0318: checkDirtyCompilationUnit(result, units[u]);
0319: }
0320: }
0321: }
0322: }
0323:
0324: private void checkDirtyCompilationUnit(RefactoringStatus result,
0325: ICompilationUnit cunit) {
0326: IResource resource = cunit.getResource();
0327: if (resource == null || resource.getType() != IResource.FILE)
0328: return;
0329: checkDirtyFile(result, (IFile) resource);
0330: }
0331:
0332: private void checkDirtyResources(final RefactoringStatus result)
0333: throws CoreException {
0334: for (int i = 0; i < fResources.length; i++) {
0335: IResource resource = fResources[i];
0336: resource.accept(new IResourceVisitor() {
0337: public boolean visit(IResource visitedResource)
0338: throws CoreException {
0339: if (visitedResource instanceof IFile) {
0340: checkDirtyFile(result, (IFile) visitedResource);
0341: }
0342: return true;
0343: }
0344: }, IResource.DEPTH_INFINITE, false);
0345: }
0346: }
0347:
0348: private void checkDirtyFile(RefactoringStatus result, IFile file) {
0349: if (file == null || !file.exists())
0350: return;
0351: ITextFileBuffer buffer = FileBuffers.getTextFileBufferManager()
0352: .getTextFileBuffer(file.getFullPath(),
0353: LocationKind.IFILE);
0354: if (buffer != null && buffer.isDirty()) {
0355: if (buffer.isStateValidated() && buffer.isSynchronized()) {
0356: result
0357: .addWarning(Messages
0358: .format(
0359: RefactoringCoreMessages.JavaDeleteProcessor_unsaved_changes,
0360: file.getFullPath().toString()));
0361: } else {
0362: result
0363: .addFatalError(Messages
0364: .format(
0365: RefactoringCoreMessages.JavaDeleteProcessor_unsaved_changes,
0366: file.getFullPath().toString()));
0367: }
0368: }
0369: }
0370:
0371: /*
0372: * The set of elements that will eventually be deleted may be very different from the set
0373: * originally selected - there may be fewer, more or different elements.
0374: * This method is used to calculate the set of elements that will be deleted - if necessary,
0375: * it asks the user.
0376: */
0377: private void recalculateElementsToDelete() throws CoreException {
0378: //the sequence is critical here
0379: fAccessorsDeleted = false;
0380: if (fDeleteSubPackages) /* add subpackages first, to allow removing elements with parents in selection etc. */
0381: addSubPackages();
0382:
0383: removeElementsWithParentsInSelection(); /*ask before adding empty cus - you don't want to ask if you, for example delete
0384: *the package, in which the cus live*/
0385: removeUnconfirmedFoldersThatContainSourceFolders(); /* a selected folder may be a parent of a source folder
0386: * we must inform the user about it and ask if ok to delete the folder*/
0387: removeUnconfirmedReferencedArchives();
0388: addEmptyCusToDelete();
0389: removeJavaElementsChildrenOfJavaElements();/*because adding cus may create elements (types in cus)
0390: *whose parents are in selection*/
0391: confirmDeletingReadOnly(); /*after empty cus - you want to ask for all cus that are to be deleted*/
0392:
0393: if (fSuggestGetterSetterDeletion)
0394: addGettersSetters();/*at the end - this cannot invalidate anything*/
0395:
0396: addDeletableParentPackagesOnPackageDeletion(); /* do not change the sequence in fJavaElements after this method */
0397: }
0398:
0399: /**
0400: * Adds all subpackages of the selected packages to the list of items to be
0401: * deleted.
0402: *
0403: * @throws JavaModelException
0404: */
0405: private void addSubPackages() throws JavaModelException {
0406:
0407: final Set javaElements = new HashSet();
0408: for (int i = 0; i < fJavaElements.length; i++) {
0409: if (fJavaElements[i] instanceof IPackageFragment) {
0410: javaElements
0411: .addAll(Arrays
0412: .asList(JavaElementUtil
0413: .getPackageAndSubpackages((IPackageFragment) fJavaElements[i])));
0414: } else {
0415: javaElements.add(fJavaElements[i]);
0416: }
0417: }
0418:
0419: fJavaElements = (IJavaElement[]) javaElements
0420: .toArray(new IJavaElement[javaElements.size()]);
0421: }
0422:
0423: /**
0424: * Add deletable parent packages to the list of items to delete.
0425: *
0426: * @throws CoreException
0427: */
0428: private void addDeletableParentPackagesOnPackageDeletion()
0429: throws CoreException {
0430:
0431: final List/* <IPackageFragment */initialPackagesToDelete = ReorgUtils
0432: .getElementsOfType(fJavaElements,
0433: IJavaElement.PACKAGE_FRAGMENT);
0434:
0435: if (initialPackagesToDelete.size() == 0)
0436: return;
0437:
0438: // Move from inner to outer packages
0439: Collections.sort(initialPackagesToDelete, new Comparator() {
0440: public int compare(Object arg0, Object arg1) {
0441: IPackageFragment one = (IPackageFragment) arg0;
0442: IPackageFragment two = (IPackageFragment) arg1;
0443: return two.getElementName().compareTo(
0444: one.getElementName());
0445: }
0446: });
0447:
0448: // Get resources and java elements which will be deleted as well
0449: final Set/* <IResource> */deletedChildren = new HashSet();
0450: deletedChildren.addAll(Arrays.asList(fResources));
0451: for (int i = 0; i < fJavaElements.length; i++) {
0452: if (!ReorgUtils.isInsideCompilationUnit(fJavaElements[i]))
0453: deletedChildren.add(fJavaElements[i].getResource());
0454: }
0455:
0456: // new package list in the right sequence
0457: final List/* <IPackageFragment */allFragmentsToDelete = new ArrayList();
0458:
0459: for (Iterator outerIter = initialPackagesToDelete.iterator(); outerIter
0460: .hasNext();) {
0461: final IPackageFragment currentPackageFragment = (IPackageFragment) outerIter
0462: .next();
0463:
0464: // The package will at least be cleared
0465: allFragmentsToDelete.add(currentPackageFragment);
0466:
0467: if (canRemoveCompletely(currentPackageFragment,
0468: initialPackagesToDelete)) {
0469:
0470: final IPackageFragment parent = JavaElementUtil
0471: .getParentSubpackage(currentPackageFragment);
0472: if (parent != null
0473: && !initialPackagesToDelete.contains(parent)) {
0474:
0475: final List/* <IPackageFragment> */emptyParents = new ArrayList();
0476: addDeletableParentPackages(parent,
0477: initialPackagesToDelete, deletedChildren,
0478: emptyParents);
0479:
0480: // Add parents in the right sequence (inner to outer)
0481: allFragmentsToDelete.addAll(emptyParents);
0482: }
0483: }
0484: }
0485:
0486: // Remove resources in deleted packages; and the packages as well
0487: final List/* <IJavaElement> */javaElements = new ArrayList();
0488: for (int i = 0; i < fJavaElements.length; i++) {
0489: if (!(fJavaElements[i] instanceof IPackageFragment)) {
0490: // remove children of deleted packages
0491: final IPackageFragment frag = (IPackageFragment) fJavaElements[i]
0492: .getAncestor(IJavaElement.PACKAGE_FRAGMENT);
0493: if (!allFragmentsToDelete.contains(frag))
0494: javaElements.add(fJavaElements[i]);
0495: }
0496: }
0497: // Re-add deleted packages - note the (new) sequence
0498: javaElements.addAll(allFragmentsToDelete);
0499:
0500: // Remove resources in deleted folders
0501: final List/* <IResource> */resources = new ArrayList();
0502: for (int i = 0; i < fResources.length; i++) {
0503: IResource parent = fResources[i];
0504: if (parent.getType() == IResource.FILE)
0505: parent = parent.getParent();
0506: if (!deletedChildren.contains(parent))
0507: resources.add(fResources[i]);
0508: }
0509:
0510: fJavaElements = (IJavaElement[]) javaElements
0511: .toArray(new IJavaElement[javaElements.size()]);
0512: fResources = (IResource[]) resources
0513: .toArray(new IResource[resources.size()]);
0514: }
0515:
0516: /**
0517: * @param pack
0518: * @param packagesToDelete
0519: * @return true if this initially selected package is really deletable
0520: * (if it has non-selected subpackages, it may only be cleared).
0521: * @throws JavaModelException
0522: */
0523: private boolean canRemoveCompletely(IPackageFragment pack,
0524: List packagesToDelete) throws JavaModelException {
0525: final IPackageFragment[] subPackages = JavaElementUtil
0526: .getPackageAndSubpackages(pack);
0527: for (int i = 0; i < subPackages.length; i++) {
0528: if (!(subPackages[i].equals(pack))
0529: && !(packagesToDelete.contains(subPackages[i])))
0530: return false;
0531: }
0532: return true;
0533: }
0534:
0535: /**
0536: * Adds deletable parent packages of the fragment "frag" to the list
0537: * "deletableParentPackages"; also adds the resources of those packages to the
0538: * set "resourcesToDelete".
0539: * @param frag
0540: * @param initialPackagesToDelete
0541: * @param resourcesToDelete
0542: * @param deletableParentPackages
0543: * @throws CoreException
0544: */
0545: private void addDeletableParentPackages(IPackageFragment frag,
0546: List initialPackagesToDelete, Set resourcesToDelete,
0547: List deletableParentPackages) throws CoreException {
0548:
0549: if (frag.getResource().isLinked()) {
0550: final IConfirmQuery query = fDeleteQueries
0551: .createYesNoQuery(
0552: RefactoringCoreMessages.JavaDeleteProcessor_confirm_linked_folder_delete,
0553: false,
0554: IReorgQueries.CONFIRM_DELETE_LINKED_PARENT);
0555: if (!query
0556: .confirm(Messages
0557: .format(
0558: RefactoringCoreMessages.JavaDeleteProcessor_delete_linked_folder_question,
0559: new String[] { frag.getResource()
0560: .getName() })))
0561: return;
0562: }
0563:
0564: final IResource[] children = (((IContainer) frag.getResource()))
0565: .members();
0566: for (int i = 0; i < children.length; i++) {
0567: // Child must be a package fragment already in the list,
0568: // or a resource which is deleted as well.
0569: if (!resourcesToDelete.contains(children[i]))
0570: return;
0571: }
0572: resourcesToDelete.add(frag.getResource());
0573: deletableParentPackages.add(frag);
0574:
0575: final IPackageFragment parent = JavaElementUtil
0576: .getParentSubpackage(frag);
0577: if (parent != null && !initialPackagesToDelete.contains(parent))
0578: addDeletableParentPackages(parent, initialPackagesToDelete,
0579: resourcesToDelete, deletableParentPackages);
0580: }
0581:
0582: // ask for confirmation of deletion of all package fragment roots that are
0583: // on classpaths of other projects
0584: private void removeUnconfirmedReferencedArchives()
0585: throws JavaModelException {
0586: String queryTitle = RefactoringCoreMessages.DeleteRefactoring_2;
0587: IConfirmQuery query = fDeleteQueries
0588: .createYesYesToAllNoNoToAllQuery(
0589: queryTitle,
0590: true,
0591: IReorgQueries.CONFIRM_DELETE_REFERENCED_ARCHIVES);
0592: removeUnconfirmedReferencedPackageFragmentRoots(query);
0593: removeUnconfirmedReferencedArchiveFiles(query);
0594: }
0595:
0596: private void removeUnconfirmedReferencedArchiveFiles(
0597: IConfirmQuery query) throws JavaModelException,
0598: OperationCanceledException {
0599: List filesToSkip = new ArrayList(0);
0600: for (int i = 0; i < fResources.length; i++) {
0601: IResource resource = fResources[i];
0602: if (!(resource instanceof IFile))
0603: continue;
0604:
0605: IJavaProject project = JavaCore.create(resource
0606: .getProject());
0607: if (project == null || !project.exists())
0608: continue;
0609: IPackageFragmentRoot root = project
0610: .findPackageFragmentRoot(resource.getFullPath());
0611: if (root == null)
0612: continue;
0613: List referencingProjects = Arrays.asList(JavaElementUtil
0614: .getReferencingProjects(root));
0615: if (skipDeletingReferencedRoot(query, root,
0616: referencingProjects))
0617: filesToSkip.add(resource);
0618: }
0619: removeFromSetToDelete((IFile[]) filesToSkip
0620: .toArray(new IFile[filesToSkip.size()]));
0621: }
0622:
0623: private void removeUnconfirmedReferencedPackageFragmentRoots(
0624: IConfirmQuery query) throws JavaModelException,
0625: OperationCanceledException {
0626: List rootsToSkip = new ArrayList(0);
0627: for (int i = 0; i < fJavaElements.length; i++) {
0628: IJavaElement element = fJavaElements[i];
0629: if (!(element instanceof IPackageFragmentRoot))
0630: continue;
0631: IPackageFragmentRoot root = (IPackageFragmentRoot) element;
0632: ArrayList referencingProjects = new ArrayList(Arrays
0633: .asList(JavaElementUtil
0634: .getReferencingProjects(root)));
0635: referencingProjects.remove(root.getJavaProject());
0636: if (skipDeletingReferencedRoot(query, root,
0637: referencingProjects))
0638: rootsToSkip.add(root);
0639: }
0640: removeFromSetToDelete((IJavaElement[]) rootsToSkip
0641: .toArray(new IJavaElement[rootsToSkip.size()]));
0642: }
0643:
0644: private static boolean skipDeletingReferencedRoot(
0645: IConfirmQuery query, IPackageFragmentRoot root,
0646: List referencingProjects) throws OperationCanceledException {
0647: if (referencingProjects.isEmpty() || root == null
0648: || !root.exists() || !root.isArchive())
0649: return false;
0650: String question = Messages.format(
0651: RefactoringCoreMessages.DeleteRefactoring_3, root
0652: .getElementName());
0653: return !query.confirm(question, referencingProjects.toArray());
0654: }
0655:
0656: private void removeUnconfirmedFoldersThatContainSourceFolders()
0657: throws CoreException {
0658: String queryTitle = RefactoringCoreMessages.DeleteRefactoring_4;
0659: IConfirmQuery query = fDeleteQueries
0660: .createYesYesToAllNoNoToAllQuery(
0661: queryTitle,
0662: true,
0663: IReorgQueries.CONFIRM_DELETE_FOLDERS_CONTAINING_SOURCE_FOLDERS);
0664: List foldersToSkip = new ArrayList(0);
0665: for (int i = 0; i < fResources.length; i++) {
0666: IResource resource = fResources[i];
0667: if (resource instanceof IFolder) {
0668: IFolder folder = (IFolder) resource;
0669: if (containsSourceFolder(folder)) {
0670: String question = Messages
0671: .format(
0672: RefactoringCoreMessages.DeleteRefactoring_5,
0673: folder.getName());
0674: if (!query.confirm(question))
0675: foldersToSkip.add(folder);
0676: }
0677: }
0678: }
0679: removeFromSetToDelete((IResource[]) foldersToSkip
0680: .toArray(new IResource[foldersToSkip.size()]));
0681: }
0682:
0683: private static boolean containsSourceFolder(IFolder folder)
0684: throws CoreException {
0685: IResource[] subFolders = folder.members();
0686: for (int i = 0; i < subFolders.length; i++) {
0687: if (!(subFolders[i] instanceof IFolder))
0688: continue;
0689: IJavaElement element = JavaCore.create(folder);
0690: if (element instanceof IPackageFragmentRoot)
0691: return true;
0692: if (element instanceof IPackageFragment)
0693: continue;
0694: if (containsSourceFolder((IFolder) subFolders[i]))
0695: return true;
0696: }
0697: return false;
0698: }
0699:
0700: private void removeElementsWithParentsInSelection() {
0701: ParentChecker parentUtil = new ParentChecker(fResources,
0702: fJavaElements);
0703: parentUtil.removeElementsWithAncestorsOnList(false);
0704: fJavaElements = parentUtil.getJavaElements();
0705: fResources = parentUtil.getResources();
0706: }
0707:
0708: private void removeJavaElementsChildrenOfJavaElements() {
0709: ParentChecker parentUtil = new ParentChecker(fResources,
0710: fJavaElements);
0711: parentUtil.removeElementsWithAncestorsOnList(true);
0712: fJavaElements = parentUtil.getJavaElements();
0713: }
0714:
0715: public Change createChange(IProgressMonitor monitor)
0716: throws CoreException {
0717: try {
0718: monitor
0719: .beginTask(
0720: RefactoringCoreMessages.JavaDeleteProcessor_creating_change,
0721: 1);
0722: final Map arguments = new HashMap();
0723: final String description = fElements.length == 1 ? RefactoringCoreMessages.JavaDeleteProcessor_description_singular
0724: : RefactoringCoreMessages.JavaDeleteProcessor_description_plural;
0725: final IProject resource = getSingleProject();
0726: final String project = resource != null ? resource
0727: .getName() : null;
0728: final String source = project != null ? Messages
0729: .format(
0730: RefactoringCoreMessages.JavaDeleteProcessor_project_pattern,
0731: project)
0732: : RefactoringCoreMessages.JavaDeleteProcessor_workspace;
0733: final String header = Messages.format(
0734: RefactoringCoreMessages.JavaDeleteProcessor_header,
0735: new String[] { String.valueOf(fElements.length),
0736: source });
0737: int flags = JavaRefactoringDescriptor.JAR_MIGRATION
0738: | JavaRefactoringDescriptor.JAR_REFACTORING
0739: | RefactoringDescriptor.STRUCTURAL_CHANGE
0740: | RefactoringDescriptor.MULTI_CHANGE;
0741: final JDTRefactoringDescriptorComment comment = new JDTRefactoringDescriptorComment(
0742: project, this , header);
0743: if (fDeleteSubPackages)
0744: comment
0745: .addSetting(RefactoringCoreMessages.JavaDeleteProcessor_delete_subpackages);
0746: if (fAccessorsDeleted)
0747: comment
0748: .addSetting(RefactoringCoreMessages.JavaDeleteProcessor_delete_accessors);
0749: final DeleteDescriptor descriptor = new DeleteDescriptor(
0750: project, description, comment.asString(),
0751: arguments, flags);
0752: arguments.put(ATTRIBUTE_DELETE_SUBPACKAGES, Boolean
0753: .valueOf(fDeleteSubPackages).toString());
0754: arguments.put(ATTRIBUTE_SUGGEST_ACCESSORS, Boolean.valueOf(
0755: fSuggestGetterSetterDeletion).toString());
0756: arguments.put(ATTRIBUTE_RESOURCES, new Integer(
0757: fResources.length).toString());
0758: for (int offset = 0; offset < fResources.length; offset++)
0759: arguments.put(
0760: JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT
0761: + (offset + 1),
0762: JavaRefactoringDescriptorUtil.resourceToHandle(
0763: project, fResources[offset]));
0764: arguments.put(ATTRIBUTE_ELEMENTS, new Integer(
0765: fJavaElements.length).toString());
0766: for (int offset = 0; offset < fJavaElements.length; offset++)
0767: arguments.put(
0768: JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT
0769: + (offset + fResources.length + 1),
0770: JavaRefactoringDescriptorUtil.elementToHandle(
0771: project, fJavaElements[offset]));
0772: return new DynamicValidationRefactoringChange(descriptor,
0773: RefactoringCoreMessages.DeleteRefactoring_7,
0774: new Change[] { fDeleteChange });
0775: } finally {
0776: monitor.done();
0777: }
0778: }
0779:
0780: private IProject getSingleProject() {
0781: IProject first = null;
0782: for (int index = 0; index < fElements.length; index++) {
0783: IProject project = null;
0784: if (fElements[index] instanceof IJavaElement)
0785: project = ((IJavaElement) fElements[index])
0786: .getJavaProject().getProject();
0787: else if (fElements[index] instanceof IResource)
0788: project = ((IResource) fElements[index]).getProject();
0789: if (project != null) {
0790: if (first == null)
0791: first = project;
0792: else if (!project.equals(first))
0793: return null;
0794: }
0795: }
0796: return first;
0797: }
0798:
0799: private void addToSetToDelete(IJavaElement[] newElements) {
0800: fJavaElements = ReorgUtils.union(fJavaElements, newElements);
0801: }
0802:
0803: private void removeFromSetToDelete(IResource[] resourcesToNotDelete) {
0804: fResources = ReorgUtils.setMinus(fResources,
0805: resourcesToNotDelete);
0806: }
0807:
0808: private void removeFromSetToDelete(
0809: IJavaElement[] elementsToNotDelete) {
0810: fJavaElements = ReorgUtils.setMinus(fJavaElements,
0811: elementsToNotDelete);
0812: }
0813:
0814: private void addGettersSetters() throws JavaModelException {
0815: IField[] fields = getFields(fJavaElements);
0816: if (fields.length == 0)
0817: return;
0818: //IField -> IMethod[]
0819: Map getterSetterMapping = createGetterSetterMapping(fields);
0820: if (getterSetterMapping.isEmpty())
0821: return;
0822: removeAlreadySelectedMethods(getterSetterMapping);
0823: if (getterSetterMapping.isEmpty())
0824: return;
0825: fAccessorsDeleted = true;
0826: List gettersSettersToAdd = getGettersSettersToDelete(getterSetterMapping);
0827: addToSetToDelete((IMethod[]) gettersSettersToAdd
0828: .toArray(new IMethod[gettersSettersToAdd.size()]));
0829: }
0830:
0831: private List getGettersSettersToDelete(Map getterSetterMapping) {
0832: List gettersSettersToAdd = new ArrayList(getterSetterMapping
0833: .size());
0834: String queryTitle = RefactoringCoreMessages.DeleteRefactoring_8;
0835: IConfirmQuery getterSetterQuery = fDeleteQueries
0836: .createYesYesToAllNoNoToAllQuery(queryTitle, true,
0837: IReorgQueries.CONFIRM_DELETE_GETTER_SETTER);
0838: for (Iterator iter = getterSetterMapping.keySet().iterator(); iter
0839: .hasNext();) {
0840: IField field = (IField) iter.next();
0841: Assert.isTrue(hasGetter(getterSetterMapping, field)
0842: || hasSetter(getterSetterMapping, field));
0843: String deleteGetterSetter = Messages.format(
0844: RefactoringCoreMessages.DeleteRefactoring_9,
0845: JavaElementUtil.createFieldSignature(field));
0846: if (getterSetterQuery.confirm(deleteGetterSetter)) {
0847: if (hasGetter(getterSetterMapping, field))
0848: gettersSettersToAdd.add(getGetter(
0849: getterSetterMapping, field));
0850: if (hasSetter(getterSetterMapping, field))
0851: gettersSettersToAdd.add(getSetter(
0852: getterSetterMapping, field));
0853: }
0854: }
0855: return gettersSettersToAdd;
0856: }
0857:
0858: //note: modifies the mapping
0859: private void removeAlreadySelectedMethods(Map getterSetterMapping) {
0860: List elementsToDelete = Arrays.asList(fJavaElements);
0861: for (Iterator iter = getterSetterMapping.keySet().iterator(); iter
0862: .hasNext();) {
0863: IField field = (IField) iter.next();
0864: //remove getter
0865: IMethod getter = getGetter(getterSetterMapping, field);
0866: if (getter != null && elementsToDelete.contains(getter))
0867: removeGetterFromMapping(getterSetterMapping, field);
0868:
0869: //remove setter
0870: IMethod setter = getSetter(getterSetterMapping, field);
0871: if (setter != null && elementsToDelete.contains(setter))
0872: removeSetterFromMapping(getterSetterMapping, field);
0873:
0874: //both getter and setter already included
0875: if (!hasGetter(getterSetterMapping, field)
0876: && !hasSetter(getterSetterMapping, field))
0877: iter.remove();
0878: }
0879: }
0880:
0881: /*
0882: * IField -> IMethod[] (array of 2 - [getter, setter], one of which can be null)
0883: */
0884: private static Map createGetterSetterMapping(IField[] fields)
0885: throws JavaModelException {
0886: Map result = new HashMap();
0887: for (int i = 0; i < fields.length; i++) {
0888: IField field = fields[i];
0889: IMethod[] getterSetter = getGetterSetter(field);
0890: if (getterSetter != null)
0891: result.put(field, getterSetter);
0892: }
0893: return result;
0894: }
0895:
0896: private static boolean hasSetter(Map getterSetterMapping,
0897: IField field) {
0898: return getterSetterMapping.containsKey(field)
0899: && getSetter(getterSetterMapping, field) != null;
0900: }
0901:
0902: private static boolean hasGetter(Map getterSetterMapping,
0903: IField field) {
0904: return getterSetterMapping.containsKey(field)
0905: && getGetter(getterSetterMapping, field) != null;
0906: }
0907:
0908: private static void removeGetterFromMapping(
0909: Map getterSetterMapping, IField field) {
0910: ((IMethod[]) getterSetterMapping.get(field))[0] = null;
0911: }
0912:
0913: private static void removeSetterFromMapping(
0914: Map getterSetterMapping, IField field) {
0915: ((IMethod[]) getterSetterMapping.get(field))[1] = null;
0916: }
0917:
0918: private static IMethod getGetter(Map getterSetterMapping,
0919: IField field) {
0920: return ((IMethod[]) getterSetterMapping.get(field))[0];
0921: }
0922:
0923: private static IMethod getSetter(Map getterSetterMapping,
0924: IField field) {
0925: return ((IMethod[]) getterSetterMapping.get(field))[1];
0926: }
0927:
0928: private static IField[] getFields(IJavaElement[] elements) {
0929: List fields = new ArrayList(3);
0930: for (int i = 0; i < elements.length; i++) {
0931: if (elements[i] instanceof IField)
0932: fields.add(elements[i]);
0933: }
0934: return (IField[]) fields.toArray(new IField[fields.size()]);
0935: }
0936:
0937: /*
0938: * returns an array of 2 [getter, setter] or null if no getter or setter exists
0939: */
0940: private static IMethod[] getGetterSetter(IField field)
0941: throws JavaModelException {
0942: IMethod getter = GetterSetterUtil.getGetter(field);
0943: IMethod setter = GetterSetterUtil.getSetter(field);
0944: if ((getter != null && getter.exists())
0945: || (setter != null && setter.exists()))
0946: return new IMethod[] { getter, setter };
0947: else
0948: return null;
0949: }
0950:
0951: //----------- read-only confirmation business ------
0952: private void confirmDeletingReadOnly() throws CoreException {
0953: if (!ReadOnlyResourceFinder.confirmDeleteOfReadOnlyElements(
0954: fJavaElements, fResources, fDeleteQueries))
0955: throw new OperationCanceledException(); //saying 'no' to this one is like cancelling the whole operation
0956: }
0957:
0958: //----------- empty CUs related method
0959: private void addEmptyCusToDelete() throws JavaModelException {
0960: Set cusToEmpty = getCusToEmpty();
0961: addToSetToDelete((ICompilationUnit[]) cusToEmpty
0962: .toArray(new ICompilationUnit[cusToEmpty.size()]));
0963: }
0964:
0965: private Set getCusToEmpty() throws JavaModelException {
0966: Set result = new HashSet();
0967: for (int i = 0; i < fJavaElements.length; i++) {
0968: IJavaElement element = fJavaElements[i];
0969: ICompilationUnit cu = ReorgUtils
0970: .getCompilationUnit(element);
0971: if (cu != null && !result.contains(cu)
0972: && willHaveAllTopLevelTypesDeleted(cu))
0973: result.add(cu);
0974: }
0975: return result;
0976: }
0977:
0978: private boolean willHaveAllTopLevelTypesDeleted(ICompilationUnit cu)
0979: throws JavaModelException {
0980: Set elementSet = new HashSet(Arrays.asList(fJavaElements));
0981: IType[] topLevelTypes = cu.getTypes();
0982: for (int i = 0; i < topLevelTypes.length; i++) {
0983: if (!elementSet.contains(topLevelTypes[i]))
0984: return false;
0985: }
0986: return true;
0987: }
0988:
0989: public boolean canEnableComment() {
0990: return true;
0991: }
0992:
0993: public String getComment() {
0994: return fComment;
0995: }
0996:
0997: public void setComment(String comment) {
0998: fComment = comment;
0999: }
1000:
1001: public RefactoringStatus initialize(RefactoringArguments arguments) {
1002: setQueries(new NullReorgQueries());
1003: final RefactoringStatus status = new RefactoringStatus();
1004: if (arguments instanceof JavaRefactoringArguments) {
1005: final JavaRefactoringArguments extended = (JavaRefactoringArguments) arguments;
1006: final String subPackages = extended
1007: .getAttribute(ATTRIBUTE_DELETE_SUBPACKAGES);
1008: if (subPackages != null) {
1009: fDeleteSubPackages = Boolean.valueOf(subPackages)
1010: .booleanValue();
1011: } else
1012: return RefactoringStatus
1013: .createFatalErrorStatus(Messages
1014: .format(
1015: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1016: ATTRIBUTE_DELETE_SUBPACKAGES));
1017: final String suggest = extended
1018: .getAttribute(ATTRIBUTE_SUGGEST_ACCESSORS);
1019: if (suggest != null) {
1020: fSuggestGetterSetterDeletion = Boolean.valueOf(suggest)
1021: .booleanValue();
1022: } else
1023: return RefactoringStatus
1024: .createFatalErrorStatus(Messages
1025: .format(
1026: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1027: ATTRIBUTE_SUGGEST_ACCESSORS));
1028: int resourceCount = 0;
1029: int elementCount = 0;
1030: String value = extended.getAttribute(ATTRIBUTE_RESOURCES);
1031: if (value != null && !"".equals(value)) {//$NON-NLS-1$
1032: try {
1033: resourceCount = Integer.parseInt(value);
1034: } catch (NumberFormatException exception) {
1035: return RefactoringStatus
1036: .createFatalErrorStatus(Messages
1037: .format(
1038: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1039: ATTRIBUTE_RESOURCES));
1040: }
1041: } else
1042: return RefactoringStatus
1043: .createFatalErrorStatus(Messages
1044: .format(
1045: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1046: ATTRIBUTE_RESOURCES));
1047: value = extended.getAttribute(ATTRIBUTE_ELEMENTS);
1048: if (value != null && !"".equals(value)) {//$NON-NLS-1$
1049: try {
1050: elementCount = Integer.parseInt(value);
1051: } catch (NumberFormatException exception) {
1052: return RefactoringStatus
1053: .createFatalErrorStatus(Messages
1054: .format(
1055: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1056: ATTRIBUTE_ELEMENTS));
1057: }
1058: } else
1059: return RefactoringStatus
1060: .createFatalErrorStatus(Messages
1061: .format(
1062: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1063: ATTRIBUTE_ELEMENTS));
1064: String handle = null;
1065: List elements = new ArrayList();
1066: for (int index = 0; index < resourceCount; index++) {
1067: final String attribute = JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT
1068: + (index + 1);
1069: handle = extended.getAttribute(attribute);
1070: if (handle != null && !"".equals(handle)) { //$NON-NLS-1$
1071: final IResource resource = JavaRefactoringDescriptorUtil
1072: .handleToResource(extended.getProject(),
1073: handle);
1074: if (resource == null || !resource.exists())
1075: status.merge(ScriptableRefactoring
1076: .createInputWarningStatus(resource,
1077: getRefactoring().getName(),
1078: IJavaRefactorings.DELETE));
1079: else
1080: elements.add(resource);
1081: } else
1082: return RefactoringStatus
1083: .createFatalErrorStatus(Messages
1084: .format(
1085: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1086: attribute));
1087: }
1088: fResources = (IResource[]) elements
1089: .toArray(new IResource[elements.size()]);
1090: elements = new ArrayList();
1091: for (int index = 0; index < elementCount; index++) {
1092: final String attribute = JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT
1093: + (resourceCount + index + 1);
1094: handle = extended.getAttribute(attribute);
1095: if (handle != null && !"".equals(handle)) { //$NON-NLS-1$
1096: final IJavaElement element = JavaRefactoringDescriptorUtil
1097: .handleToElement(extended.getProject(),
1098: handle, false);
1099: if (element == null || !element.exists())
1100: status.merge(ScriptableRefactoring
1101: .createInputWarningStatus(element,
1102: getRefactoring().getName(),
1103: IJavaRefactorings.DELETE));
1104: else
1105: elements.add(element);
1106: } else
1107: return RefactoringStatus
1108: .createFatalErrorStatus(Messages
1109: .format(
1110: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1111: attribute));
1112: }
1113: fJavaElements = (IJavaElement[]) elements
1114: .toArray(new IJavaElement[elements.size()]);
1115: fElements = new Object[fResources.length
1116: + fJavaElements.length];
1117: System.arraycopy(fResources, 0, fElements, 0,
1118: fResources.length);
1119: System.arraycopy(fJavaElements, 0, fElements,
1120: fResources.length, fJavaElements.length);
1121: } else
1122: return RefactoringStatus
1123: .createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments);
1124: return status;
1125: }
1126: }
|