]> git.uio.no Git - ifi-stolz-refaktor.git/blobdiff - case-study/jdt-before/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpRefactoring.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-before / core extension / org / eclipse / jdt / internal / corext / fix / CleanUpRefactoring.java
diff --git a/case-study/jdt-before/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpRefactoring.java b/case-study/jdt-before/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpRefactoring.java
new file mode 100644 (file)
index 0000000..288e08d
--- /dev/null
@@ -0,0 +1,885 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.fix;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.eclipse.swt.widgets.Display;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ProjectScope;
+import org.eclipse.core.resources.ResourcesPlugin;
+
+import org.eclipse.text.edits.MalformedTreeException;
+import org.eclipse.text.edits.TextEdit;
+import org.eclipse.text.edits.TextEditGroup;
+import org.eclipse.text.edits.UndoEdit;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.IDocument;
+
+import org.eclipse.ltk.core.refactoring.CategorizedTextEditGroup;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.CompositeChange;
+import org.eclipse.ltk.core.refactoring.ContentStamp;
+import org.eclipse.ltk.core.refactoring.GroupCategory;
+import org.eclipse.ltk.core.refactoring.GroupCategorySet;
+import org.eclipse.ltk.core.refactoring.NullChange;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.RefactoringTickProvider;
+import org.eclipse.ltk.core.refactoring.TextChange;
+import org.eclipse.ltk.core.refactoring.TextEditBasedChangeGroup;
+import org.eclipse.ltk.core.refactoring.TextFileChange;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.WorkingCopyOwner;
+import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.ASTRequestor;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.refactoring.CompilationUnitChange;
+
+import org.eclipse.jdt.internal.corext.dom.ASTBatchParser;
+import org.eclipse.jdt.internal.corext.refactoring.Checks;
+import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationStateChange;
+import org.eclipse.jdt.internal.corext.refactoring.changes.MultiStateCompilationUnitChange;
+import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
+import org.eclipse.jdt.internal.corext.refactoring.util.TextEditUtil;
+import org.eclipse.jdt.internal.corext.util.Messages;
+
+import org.eclipse.jdt.ui.JavaElementLabels;
+import org.eclipse.jdt.ui.cleanup.CleanUpContext;
+import org.eclipse.jdt.ui.cleanup.CleanUpOptions;
+import org.eclipse.jdt.ui.cleanup.ICleanUp;
+import org.eclipse.jdt.ui.cleanup.ICleanUpFix;
+import org.eclipse.jdt.ui.text.java.IProblemLocation;
+
+import org.eclipse.jdt.internal.ui.JavaPlugin;
+import org.eclipse.jdt.internal.ui.fix.IMultiFix.MultiFixContext;
+import org.eclipse.jdt.internal.ui.fix.MapCleanUpOptions;
+import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
+import org.eclipse.jdt.internal.ui.refactoring.IScheduledRefactoring;
+import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
+
+public class CleanUpRefactoring extends Refactoring implements IScheduledRefactoring {
+
+       public static class CleanUpTarget {
+
+               private final ICompilationUnit fCompilationUnit;
+
+               public CleanUpTarget(ICompilationUnit unit) {
+                       fCompilationUnit= unit;
+               }
+
+               public ICompilationUnit getCompilationUnit() {
+                       return fCompilationUnit;
+               }
+       }
+
+       public static class MultiFixTarget extends CleanUpTarget {
+
+               private final IProblemLocation[] fProblems;
+
+               public MultiFixTarget(ICompilationUnit unit, IProblemLocation[] problems) {
+                       super(unit);
+                       fProblems= problems;
+               }
+
+               public IProblemLocation[] getProblems() {
+                       return fProblems;
+               }
+       }
+
+       public static class CleanUpChange extends CompilationUnitChange {
+
+               private UndoEdit fUndoEdit;
+
+               public CleanUpChange(String name, ICompilationUnit cunit) {
+               super(name, cunit);
+        }
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               protected Change createUndoChange(UndoEdit edit, ContentStamp stampToRestore) {
+                   fUndoEdit= edit;
+                       return super.createUndoChange(edit, stampToRestore);
+               }
+
+               public UndoEdit getUndoEdit() {
+               return fUndoEdit;
+        }
+
+               /*
+                * @see org.eclipse.ltk.core.refactoring.TextChange#perform(org.eclipse.core.runtime.IProgressMonitor)
+                */
+               @Override
+               public Change perform(final IProgressMonitor pm) throws CoreException {
+                       if (Display.getCurrent() == null) {
+                               final Change[] result= new Change[1];
+                               final CoreException[] exs= new CoreException[1];
+                               Display.getDefault().syncExec(new Runnable() {
+                                       public void run() {
+                                               try {
+                                                       result[0]= CleanUpChange.super.perform(pm);
+                                               } catch (CoreException e) {
+                                                       exs[0]= e;
+                                               }
+                                       }
+                               });
+
+                               if (exs[0] != null)
+                                       throw exs[0];
+
+                               return result[0];
+                       } else {
+                               return super.perform(pm);
+                       }
+               }
+       }
+
+       private static class FixCalculationException extends RuntimeException {
+
+               private static final long serialVersionUID= 3807273310144726165L;
+
+               private final CoreException fException;
+
+               public FixCalculationException(CoreException exception) {
+                       fException= exception;
+               }
+
+               public CoreException getException() {
+                       return fException;
+               }
+       }
+
+       private static class ParseListElement {
+
+               private final CleanUpTarget fTarget;
+               private final ICleanUp[] fCleanUpsArray;
+
+               public ParseListElement(CleanUpTarget cleanUpTarget, ICleanUp[] cleanUps) {
+                       fTarget= cleanUpTarget;
+                       fCleanUpsArray= cleanUps;
+               }
+
+               public CleanUpTarget getTarget() {
+                       return fTarget;
+               }
+
+               public ICleanUp[] getCleanUps() {
+                       return fCleanUpsArray;
+               }
+       }
+
+       private final class CleanUpRefactoringProgressMonitor extends SubProgressMonitor {
+
+               private double fRealWork;
+               private int fFlushCount;
+               private final int fSize;
+               private final int fIndex;
+
+               private CleanUpRefactoringProgressMonitor(IProgressMonitor monitor, int ticks, int size, int index) {
+                       super(monitor, ticks);
+                       fFlushCount= 0;
+                       fSize= size;
+                       fIndex= index;
+               }
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               public void internalWorked(double work) {
+                       fRealWork+= work;
+               }
+
+               public void flush() {
+                       super.internalWorked(fRealWork);
+                       reset();
+                       fFlushCount++;
+               }
+
+               public void reset() {
+                       fRealWork= 0.0;
+               }
+
+               @Override
+               public void done() {}
+
+               public int getIndex() {
+                       return fIndex + fFlushCount;
+               }
+
+               public String getSubTaskMessage(ICompilationUnit source) {
+                       String typeName= BasicElementLabels.getFileName(source);
+                       return Messages.format(FixMessages.CleanUpRefactoring_ProcessingCompilationUnit_message, new Object[] {new Integer(getIndex()), new Integer(fSize), typeName});
+               }
+       }
+
+       private static class CleanUpASTRequestor extends ASTRequestor {
+
+               private final List<ParseListElement> fUndoneElements;
+               private final Hashtable<ICompilationUnit, List<CleanUpChange>> fSolutions;
+               private final Hashtable<ICompilationUnit, ParseListElement> fCompilationUnitParseElementMap;
+               private final CleanUpRefactoringProgressMonitor fMonitor;
+
+               public CleanUpASTRequestor(List<ParseListElement> parseList, Hashtable<ICompilationUnit, List<CleanUpChange>> solutions, CleanUpRefactoringProgressMonitor monitor) {
+                       fSolutions= solutions;
+                       fMonitor= monitor;
+                       fUndoneElements= new ArrayList<ParseListElement>();
+                       fCompilationUnitParseElementMap= new Hashtable<ICompilationUnit, ParseListElement>(parseList.size());
+                       for (Iterator<ParseListElement> iter= parseList.iterator(); iter.hasNext();) {
+                               ParseListElement element= iter.next();
+                               fCompilationUnitParseElementMap.put(element.getTarget().getCompilationUnit(), element);
+                       }
+               }
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               public void acceptAST(ICompilationUnit source, CompilationUnit ast) {
+
+                       fMonitor.subTask(fMonitor.getSubTaskMessage(source));
+
+                       ICompilationUnit primary= (ICompilationUnit)source.getPrimaryElement();
+                       ParseListElement element= fCompilationUnitParseElementMap.get(primary);
+                       CleanUpTarget target= element.getTarget();
+
+                       CleanUpContext context;
+                       if (target instanceof MultiFixTarget) {
+                               context= new MultiFixContext(source, ast, ((MultiFixTarget)target).getProblems());
+                       } else {
+                               context= new CleanUpContext(source, ast);
+                       }
+                       ICleanUp[] rejectedCleanUps= calculateSolutions(context, element.getCleanUps());
+
+                       if (rejectedCleanUps.length > 0) {
+                               fUndoneElements.add(new ParseListElement(target, rejectedCleanUps));
+                               fMonitor.reset();
+                       } else {
+                               fMonitor.flush();
+                       }
+               }
+
+               public void acceptSource(ICompilationUnit source) {
+                       acceptAST(source, null);
+               }
+
+               public List<ParseListElement> getUndoneElements() {
+                       return fUndoneElements;
+               }
+
+               private ICleanUp[] calculateSolutions(CleanUpContext context, ICleanUp[] cleanUps) {
+                       List<ICleanUp>result= new ArrayList<ICleanUp>();
+                       CleanUpChange solution;
+                       try {
+                               solution= calculateChange(context, cleanUps, result, null);
+                       } catch (CoreException e) {
+                               throw new FixCalculationException(e);
+                       }
+
+                       if (solution != null) {
+                               integrateSolution(solution, context.getCompilationUnit());
+                       }
+
+                       return result.toArray(new ICleanUp[result.size()]);
+               }
+
+               private void integrateSolution(CleanUpChange solution, ICompilationUnit source) {
+                       ICompilationUnit primary= source.getPrimary();
+
+                       List<CleanUpChange> changes= fSolutions.get(primary);
+                       if (changes == null) {
+                               changes= new ArrayList<CleanUpChange>();
+                               fSolutions.put(primary, changes);
+                       }
+                       changes.add(solution);
+               }
+       }
+
+       private class CleanUpFixpointIterator {
+
+               private List<ParseListElement> fParseList;
+               private final Hashtable<ICompilationUnit, List<CleanUpChange>> fSolutions;
+               private final Hashtable<ICompilationUnit, ICompilationUnit> fWorkingCopies; // map from primary to working copy
+               private final Map<String, String> fCleanUpOptions;
+               private final int fSize;
+               private int fIndex;
+
+               public CleanUpFixpointIterator(CleanUpTarget[] targets, ICleanUp[] cleanUps) {
+                       fSolutions= new Hashtable<ICompilationUnit, List<CleanUpChange>>(targets.length);
+                       fWorkingCopies= new Hashtable<ICompilationUnit, ICompilationUnit>();
+
+                       fParseList= new ArrayList<ParseListElement>(targets.length);
+                       for (int i= 0; i < targets.length; i++) {
+                               fParseList.add(new ParseListElement(targets[i], cleanUps));
+                       }
+
+                       fCleanUpOptions= new Hashtable<String, String>();
+                       for (int i= 0; i < cleanUps.length; i++) {
+                               ICleanUp cleanUp= cleanUps[i];
+                               Map<String, String> currentCleanUpOption= cleanUp.getRequirements().getCompilerOptions();
+                               if (currentCleanUpOption != null)
+                                       fCleanUpOptions.putAll(currentCleanUpOption);
+                       }
+
+                       fSize= targets.length;
+                       fIndex= 1;
+               }
+
+               public boolean hasNext() {
+                       return !fParseList.isEmpty();
+               }
+
+               public void next(IProgressMonitor monitor) throws CoreException {
+                       List<ICompilationUnit> parseList= new ArrayList<ICompilationUnit>();
+                       List<ICompilationUnit> sourceList= new ArrayList<ICompilationUnit>();
+
+                       try {
+                               for (Iterator<ParseListElement> iter= fParseList.iterator(); iter.hasNext();) {
+                                       ParseListElement element= iter.next();
+
+                                       ICompilationUnit compilationUnit= element.getTarget().getCompilationUnit();
+                                       if (fSolutions.containsKey(compilationUnit)) {
+                                               if (fWorkingCopies.containsKey(compilationUnit)) {
+                                                       compilationUnit= fWorkingCopies.get(compilationUnit);
+                                               } else {
+                                                       compilationUnit= compilationUnit.getWorkingCopy(new WorkingCopyOwner() {}, null);
+                                                       fWorkingCopies.put(compilationUnit.getPrimary(), compilationUnit);
+                                               }
+                                               applyChange(compilationUnit, fSolutions.get(compilationUnit.getPrimary()));
+                                       }
+
+                                       if (requiresAST(element.getCleanUps())) {
+                                               parseList.add(compilationUnit);
+                                       } else {
+                                               sourceList.add(compilationUnit);
+                                       }
+                               }
+
+                               CleanUpRefactoringProgressMonitor cuMonitor= new CleanUpRefactoringProgressMonitor(monitor, parseList.size() + sourceList.size(), fSize, fIndex);
+                               CleanUpASTRequestor requestor= new CleanUpASTRequestor(fParseList, fSolutions, cuMonitor);
+                               if (parseList.size() > 0) {
+                                       ASTBatchParser parser= new ASTBatchParser() {
+                                               @Override
+                                               protected ASTParser createParser(IJavaProject project) {
+                                                       ASTParser result= createCleanUpASTParser();
+                                                       result.setProject(project);
+
+                                                       Map<String, String> options= RefactoringASTParser.getCompilerOptions(project);
+                                                       options.putAll(fCleanUpOptions);
+                                                       result.setCompilerOptions(options);
+                                                       return result;
+                                               }
+                                       };
+                                       try {
+                                               ICompilationUnit[] units= parseList.toArray(new ICompilationUnit[parseList.size()]);
+                                               parser.createASTs(units, new String[0], requestor, cuMonitor);
+                                       } catch (FixCalculationException e) {
+                                               throw e.getException();
+                                       }
+                               }
+
+                               for (Iterator<ICompilationUnit> iterator= sourceList.iterator(); iterator.hasNext();) {
+                                       ICompilationUnit cu= iterator.next();
+
+                                       monitor.worked(1);
+
+                                       requestor.acceptSource(cu);
+
+                                       if (monitor.isCanceled())
+                                               throw new OperationCanceledException();
+                               }
+
+                               fParseList= requestor.getUndoneElements();
+                               fIndex= cuMonitor.getIndex();
+                       } finally {
+                       }
+               }
+
+               public void dispose() {
+                       for (Iterator<ICompilationUnit> iterator= fWorkingCopies.values().iterator(); iterator.hasNext();) {
+                               ICompilationUnit cu= iterator.next();
+                               try {
+                                       cu.discardWorkingCopy();
+                               } catch (JavaModelException e) {
+                                       JavaPlugin.log(e);
+                               }
+                       }
+                       fWorkingCopies.clear();
+               }
+
+               private boolean requiresAST(ICleanUp[] cleanUps) {
+                       for (int i= 0; i < cleanUps.length; i++) {
+                               if (cleanUps[i].getRequirements().requiresAST())
+                                       return true;
+                       }
+                       return false;
+               }
+
+               public Change[] getResult() {
+
+                       Change[] result= new Change[fSolutions.size()];
+                       int i=0;
+                       for (Iterator<Entry<ICompilationUnit, List<CleanUpChange>>> iterator= fSolutions.entrySet().iterator(); iterator.hasNext();) {
+                               Entry<ICompilationUnit, List<CleanUpChange>>  entry= iterator.next();
+
+                               List<CleanUpChange> changes= entry.getValue();
+                               ICompilationUnit unit= entry.getKey();
+
+                               int saveMode;
+                               if (fLeaveFilesDirty) {
+                                       saveMode= TextFileChange.LEAVE_DIRTY;
+                               } else {
+                                       saveMode= TextFileChange.KEEP_SAVE_STATE;
+                               }
+
+                               if (changes.size() == 1) {
+                                       CleanUpChange change= changes.get(0);
+                                       change.setSaveMode(saveMode);
+                                       result[i]= change;
+                               } else {
+                                       MultiStateCompilationUnitChange mscuc= new MultiStateCompilationUnitChange(getChangeName(unit), unit);
+                                       for (int j= 0; j < changes.size(); j++) {
+                                               mscuc.addChange(createGroupFreeChange(changes.get(j)));
+                                       }
+                                       mscuc.setSaveMode(saveMode);
+                                       result[i]= mscuc;
+                               }
+
+                               i++;
+                       }
+
+                       return result;
+               }
+
+               private TextChange createGroupFreeChange(CleanUpChange change) {
+                       CleanUpChange result= new CleanUpChange(change.getName(), change.getCompilationUnit());
+                       result.setEdit(change.getEdit());
+                       result.setSaveMode(change.getSaveMode());
+               return result;
+        }
+
+               private void applyChange(ICompilationUnit compilationUnit, List<CleanUpChange> changes) throws JavaModelException, CoreException {
+                       IDocument document= new Document(changes.get(0).getCurrentContent(new NullProgressMonitor()));
+                       for (int i= 0; i < changes.size(); i++) {
+                               CleanUpChange change= changes.get(i);
+                               TextEdit edit= change.getEdit().copy();
+
+                               try {
+                                       edit.apply(document, TextEdit.UPDATE_REGIONS);
+                               } catch (MalformedTreeException e) {
+                                       JavaPlugin.log(e);
+                               } catch (BadLocationException e) {
+                                       JavaPlugin.log(e);
+                               }
+                       }
+                       compilationUnit.getBuffer().setContents(document.get());
+               }
+       }
+
+       private static final RefactoringTickProvider CLEAN_UP_REFACTORING_TICK_PROVIDER= new RefactoringTickProvider(0, 1, 0, 0);
+
+       /**
+        * A clean up is considered slow if its execution lasts longer then the value of
+        * SLOW_CLEAN_UP_THRESHOLD in ms.
+        */
+       private static final int SLOW_CLEAN_UP_THRESHOLD= 2000;
+
+       private final List<ICleanUp> fCleanUps;
+       private final Hashtable<IJavaProject, List<CleanUpTarget>> fProjects;
+       private Change fChange;
+       private boolean fLeaveFilesDirty;
+       private final String fName;
+
+       private boolean fUseOptionsFromProfile;
+
+       public CleanUpRefactoring() {
+               this(FixMessages.CleanUpRefactoring_Refactoring_name);
+       }
+
+       public CleanUpRefactoring(String name) {
+               fName= name;
+               fCleanUps= new ArrayList<ICleanUp>();
+               fProjects= new Hashtable<IJavaProject, List<CleanUpTarget>>();
+               fUseOptionsFromProfile= false;
+       }
+
+       public void setUseOptionsFromProfile(boolean enabled) {
+               fUseOptionsFromProfile= enabled;
+       }
+
+       public void addCompilationUnit(ICompilationUnit unit) {
+               addCleanUpTarget(new CleanUpTarget(unit));
+       }
+
+       public void addCleanUpTarget(CleanUpTarget target) {
+
+               IJavaProject javaProject= target.getCompilationUnit().getJavaProject();
+               if (!fProjects.containsKey(javaProject))
+                       fProjects.put(javaProject, new ArrayList<CleanUpTarget>());
+
+               List<CleanUpTarget> targets= fProjects.get(javaProject);
+               targets.add(target);
+       }
+
+       public CleanUpTarget[] getCleanUpTargets() {
+               List<CleanUpTarget> result= new ArrayList<CleanUpTarget>();
+               for (Iterator<List<CleanUpTarget>> iter= fProjects.values().iterator(); iter.hasNext();) {
+                       List<CleanUpTarget> projectTargets= iter.next();
+                       result.addAll(projectTargets);
+               }
+               return result.toArray(new CleanUpTarget[result.size()]);
+       }
+
+       public int getCleanUpTargetsSize() {
+               int result= 0;
+               for (Iterator<List<CleanUpTarget>> iter= fProjects.values().iterator(); iter.hasNext();) {
+                       List<CleanUpTarget> projectTargets= iter.next();
+                       result+= projectTargets.size();
+               }
+               return result;
+       }
+
+       public void addCleanUp(ICleanUp fix) {
+               fCleanUps.add(fix);
+       }
+
+       public void clearCleanUps() {
+               fCleanUps.clear();
+       }
+
+       public boolean hasCleanUps() {
+               return !fCleanUps.isEmpty();
+       }
+
+       public ICleanUp[] getCleanUps() {
+               return fCleanUps.toArray(new ICleanUp[fCleanUps.size()]);
+       }
+
+       public IJavaProject[] getProjects() {
+               return fProjects.keySet().toArray(new IJavaProject[fProjects.keySet().size()]);
+       }
+
+       public void setLeaveFilesDirty(boolean leaveFilesDirty) {
+               fLeaveFilesDirty= leaveFilesDirty;
+       }
+
+       /* (non-Javadoc)
+        * @see org.eclipse.ltk.core.refactoring.Refactoring#getName()
+        */
+       @Override
+       public String getName() {
+               return fName;
+       }
+
+       /* (non-Javadoc)
+        * @see org.eclipse.ltk.core.refactoring.Refactoring#checkInitialConditions(org.eclipse.core.runtime.IProgressMonitor)
+        */
+       @Override
+       public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException {
+               if (pm != null) {
+                       pm.beginTask("", 1); //$NON-NLS-1$
+                       pm.worked(1);
+                       pm.done();
+               }
+               return new RefactoringStatus();
+       }
+
+       /* (non-Javadoc)
+        * @see org.eclipse.ltk.core.refactoring.Refactoring#createChange(org.eclipse.core.runtime.IProgressMonitor)
+        */
+       @Override
+       public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException {
+               if (pm != null) {
+                       pm.beginTask("", 1); //$NON-NLS-1$
+                       pm.worked(1);
+                       pm.done();
+               }
+               return fChange;
+       }
+
+       /* (non-Javadoc)
+        * @see org.eclipse.ltk.core.refactoring.Refactoring#checkFinalConditions(org.eclipse.core.runtime.IProgressMonitor)
+        */
+       @Override
+       public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException {
+
+               if (pm == null)
+                       pm= new NullProgressMonitor();
+
+               if (fProjects.size() == 0 || fCleanUps.size() == 0) {
+                       pm.beginTask("", 1); //$NON-NLS-1$
+                       pm.worked(1);
+                       pm.done();
+                       fChange= new NullChange();
+
+                       return new RefactoringStatus();
+               }
+
+               int cuCount= getCleanUpTargetsSize();
+
+               RefactoringStatus result= new RefactoringStatus();
+
+               ICleanUp[] cleanUps= getCleanUps();
+               pm.beginTask("", cuCount * 2 * fCleanUps.size() + 4 * cleanUps.length); //$NON-NLS-1$
+               try {
+                       DynamicValidationStateChange change= new DynamicValidationStateChange(getName());
+                       change.setSchedulingRule(getSchedulingRule());
+                       for (Iterator<Entry<IJavaProject, List<CleanUpTarget>>> projectIter= fProjects.entrySet().iterator(); projectIter.hasNext();) {
+                               Entry<IJavaProject, List<CleanUpTarget>> entry= projectIter.next();
+                               IJavaProject project= entry.getKey();
+                               List<CleanUpTarget> targetsList= entry.getValue();
+                               CleanUpTarget[] targets= targetsList.toArray(new CleanUpTarget[targetsList.size()]);
+
+                               if (fUseOptionsFromProfile) {
+                                       result.merge(setOptionsFromProfile(project, cleanUps));
+                                       if (result.hasFatalError())
+                                               return result;
+                               }
+
+                               result.merge(checkPreConditions(project, targets, new SubProgressMonitor(pm, 3 * cleanUps.length)));
+                               if (result.hasFatalError())
+                                       return result;
+
+                               Change[] changes= cleanUpProject(project, targets, cleanUps, pm);
+
+                               result.merge(checkPostConditions(new SubProgressMonitor(pm, cleanUps.length)));
+                               if (result.hasFatalError())
+                                       return result;
+
+                               for (int i= 0; i < changes.length; i++) {
+                                       change.add(changes[i]);
+                               }
+                       }
+                       fChange= change;
+
+                       List<IResource> files= new ArrayList<IResource>();
+                       findFilesToBeModified(change, files);
+                       result.merge(Checks.validateModifiesFiles(files.toArray(new IFile[files.size()]), getValidationContext()));
+               } finally {
+                       pm.done();
+               }
+
+               return result;
+       }
+
+       private void findFilesToBeModified(CompositeChange change, List<IResource> result) throws JavaModelException {
+               Change[] children= change.getChildren();
+               for (int i= 0; i < children.length; i++) {
+                       Change child= children[i];
+                       if (child instanceof CompositeChange) {
+                               findFilesToBeModified((CompositeChange)child, result);
+                       } else if (child instanceof MultiStateCompilationUnitChange) {
+                               result.add(((MultiStateCompilationUnitChange)child).getCompilationUnit().getCorrespondingResource());
+                       } else if (child instanceof CompilationUnitChange) {
+                               result.add(((CompilationUnitChange)child).getCompilationUnit().getCorrespondingResource());
+                       }
+               }
+       }
+
+       private Change[] cleanUpProject(IJavaProject project, CleanUpTarget[] targets, ICleanUp[] cleanUps, IProgressMonitor monitor) throws CoreException {
+               CleanUpFixpointIterator iter= new CleanUpFixpointIterator(targets, cleanUps);
+
+               SubProgressMonitor subMonitor= new SubProgressMonitor(monitor, 2 * targets.length * cleanUps.length);
+               subMonitor.beginTask("", targets.length); //$NON-NLS-1$
+               subMonitor.subTask(Messages.format(FixMessages.CleanUpRefactoring_Parser_Startup_message, BasicElementLabels.getResourceName(project.getProject())));
+               try {
+                       while (iter.hasNext()) {
+                               iter.next(subMonitor);
+                       }
+
+                       return iter.getResult();
+               } finally {
+                       iter.dispose();
+                       subMonitor.done();
+               }
+       }
+
+       private RefactoringStatus setOptionsFromProfile(IJavaProject javaProject, ICleanUp[] cleanUps) {
+               Map<String, String> options= CleanUpPreferenceUtil.loadOptions(new ProjectScope(javaProject.getProject()));
+               if (options == null)
+                       return RefactoringStatus.createFatalErrorStatus(Messages.format(FixMessages.CleanUpRefactoring_could_not_retrive_profile, BasicElementLabels.getResourceName(javaProject.getProject())));
+
+               CleanUpOptions cleanUpOptions= new MapCleanUpOptions(options);
+               for (int i= 0; i < cleanUps.length; i++)
+                       cleanUps[i].setOptions(cleanUpOptions);
+
+               return new RefactoringStatus();
+       }
+
+       private RefactoringStatus checkPreConditions(IJavaProject javaProject, CleanUpTarget[] targets, IProgressMonitor monitor) throws CoreException {
+               RefactoringStatus result= new RefactoringStatus();
+
+               ICompilationUnit[] compilationUnits= new ICompilationUnit[targets.length];
+               for (int i= 0; i < targets.length; i++) {
+                       compilationUnits[i]= targets[i].getCompilationUnit();
+               }
+
+               ICleanUp[] cleanUps= getCleanUps();
+               monitor.beginTask("", compilationUnits.length * cleanUps.length); //$NON-NLS-1$
+               monitor.subTask(Messages.format(FixMessages.CleanUpRefactoring_Initialize_message, BasicElementLabels.getResourceName(javaProject.getProject())));
+               try {
+                       for (int j= 0; j < cleanUps.length; j++) {
+                               result.merge(cleanUps[j].checkPreConditions(javaProject, compilationUnits, new SubProgressMonitor(monitor, compilationUnits.length)));
+                               if (result.hasFatalError())
+                                       return result;
+                       }
+               } finally {
+                       monitor.done();
+               }
+
+               return result;
+       }
+
+       private RefactoringStatus checkPostConditions(SubProgressMonitor monitor) throws CoreException {
+               RefactoringStatus result= new RefactoringStatus();
+
+               ICleanUp[] cleanUps= getCleanUps();
+               monitor.beginTask("", cleanUps.length); //$NON-NLS-1$
+               monitor.subTask(FixMessages.CleanUpRefactoring_checkingPostConditions_message);
+               try {
+                       for (int j= 0; j < cleanUps.length; j++) {
+                               result.merge(cleanUps[j].checkPostConditions(new SubProgressMonitor(monitor, 1)));
+                       }
+               } finally {
+                       monitor.done();
+               }
+               return result;
+       }
+
+       private static String getChangeName(ICompilationUnit compilationUnit) {
+               StringBuffer buf= new StringBuffer();
+               JavaElementLabels.getCompilationUnitLabel(compilationUnit, JavaElementLabels.ALL_DEFAULT, buf);
+               buf.append(JavaElementLabels.CONCAT_STRING);
+
+               StringBuffer buf2= new StringBuffer();
+               JavaElementLabels.getPackageFragmentLabel((IPackageFragment)compilationUnit.getParent(), JavaElementLabels.P_QUALIFIED, buf2);
+               buf.append(buf2.toString().replace('.', '/'));
+
+               return buf.toString();
+       }
+
+       public static CleanUpChange calculateChange(CleanUpContext context, ICleanUp[] cleanUps, List<ICleanUp> undoneCleanUps, HashSet<ICleanUp> slowCleanUps) throws CoreException {
+               if (cleanUps.length == 0)
+                       return null;
+
+               CleanUpChange solution= null;
+               int i= 0;
+               do {
+                       ICleanUp cleanUp= cleanUps[i];
+                       ICleanUpFix fix;
+                       if (slowCleanUps != null) {
+                               long timeBefore= System.currentTimeMillis();
+                               fix= cleanUp.createFix(context);
+                               if (System.currentTimeMillis() - timeBefore > SLOW_CLEAN_UP_THRESHOLD)
+                                       slowCleanUps.add(cleanUp);
+                       } else {
+                               fix= cleanUp.createFix(context);
+                       }
+                       if (fix != null) {
+                               CompilationUnitChange current= fix.createChange(null);
+                               TextEdit currentEdit= current.getEdit();
+
+                               if (solution != null) {
+                                       if (TextEditUtil.overlaps(currentEdit, solution.getEdit())) {
+                                               undoneCleanUps.add(cleanUp);
+                                       } else {
+                                               CleanUpChange merge= new CleanUpChange(FixMessages.CleanUpRefactoring_clean_up_multi_chang_name, context.getCompilationUnit());
+                                               merge.setEdit(TextEditUtil.merge(currentEdit, solution.getEdit()));
+
+                                               copyChangeGroups(merge, solution);
+                                               copyChangeGroups(merge, current);
+
+                                               solution= merge;
+                                       }
+                               } else {
+                                       solution= new CleanUpChange(current.getName(), context.getCompilationUnit());
+                                       solution.setEdit(currentEdit);
+
+                                       copyChangeGroups(solution, current);
+                               }
+                       }
+                       i++;
+               } while (i < cleanUps.length && (context.getAST() == null || !cleanUps[i].getRequirements().requiresFreshAST()));
+
+               for (; i < cleanUps.length; i++) {
+                       undoneCleanUps.add(cleanUps[i]);
+               }
+               return solution;
+       }
+
+       private static void copyChangeGroups(CompilationUnitChange target, CompilationUnitChange source) {
+               TextEditBasedChangeGroup[] changeGroups= source.getChangeGroups();
+               for (int i= 0; i < changeGroups.length; i++) {
+                       TextEditGroup textEditGroup= changeGroups[i].getTextEditGroup();
+                       TextEditGroup newGroup;
+                       if (textEditGroup instanceof CategorizedTextEditGroup) {
+                               String label= textEditGroup.getName();
+                               newGroup= new CategorizedTextEditGroup(label, new GroupCategorySet(new GroupCategory(label, label, label)));
+                       } else {
+                               newGroup= new TextEditGroup(textEditGroup.getName());
+                       }
+                       TextEdit[] textEdits= textEditGroup.getTextEdits();
+                       for (int j= 0; j < textEdits.length; j++) {
+                               newGroup.addTextEdit(textEdits[j]);
+                       }
+                       target.addTextEditGroup(newGroup);
+               }
+       }
+
+       /* (non-Javadoc)
+        * @see org.eclipse.ltk.core.refactoring.Refactoring#getRefactoringTickProvider()
+        */
+       @Override
+       protected RefactoringTickProvider doGetRefactoringTickProvider() {
+               return CLEAN_UP_REFACTORING_TICK_PROVIDER;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public ISchedulingRule getSchedulingRule() {
+               return ResourcesPlugin.getWorkspace().getRoot();
+       }
+
+       public static ASTParser createCleanUpASTParser() {
+               ASTParser result= ASTParser.newParser(ASTProvider.SHARED_AST_LEVEL);
+
+               result.setResolveBindings(true);
+               result.setStatementsRecovery(ASTProvider.SHARED_AST_STATEMENT_RECOVERY);
+               result.setBindingsRecovery(ASTProvider.SHARED_BINDING_RECOVERY);
+
+               return result;
+       }
+
+}