--- /dev/null
+/*******************************************************************************
+ * 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;
+ }
+
+}