blob: 4482e055544e9f261db24ce18d98f6e523d4bf9c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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.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.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
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.core.manipulation.util.BasicElementLabels;
import org.eclipse.jdt.internal.corext.dom.ASTBatchParser;
import org.eclipse.jdt.internal.corext.dom.IASTSharedValues;
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.IJavaStatusConstants;
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.refactoring.IScheduledRefactoring;
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);
}
@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(() -> {
try {
result[0]= CleanUpChange.super.perform(pm);
} catch (CoreException e) {
exs[0]= e;
}
});
if (exs[0] != null) {
IStatus status= new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IJavaStatusConstants.INTERNAL_ERROR, Messages.format(FixMessages.CleanUpRefactoring_exception,
getCompilationUnit().getResource().getFullPath().toString()), exs[0]);
throw new CoreException(status);
}
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 static 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;
}
@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[] {Integer.valueOf(getIndex()), Integer.valueOf(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<>();
fCompilationUnitParseElementMap= new Hashtable<>(parseList.size());
for (ParseListElement element : parseList) {
fCompilationUnitParseElementMap.put(element.getTarget().getCompilationUnit(), element);
}
}
@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<>();
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<>();
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<>(targets.length);
fWorkingCopies= new Hashtable<>();
fParseList= new ArrayList<>(targets.length);
for (CleanUpTarget target : targets) {
fParseList.add(new ParseListElement(target, cleanUps));
}
fCleanUpOptions= new Hashtable<>();
for (ICleanUp cleanUp : cleanUps) {
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<>();
List<ICompilationUnit> sourceList= new ArrayList<>();
try {
for (ParseListElement element : fParseList) {
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 (ICompilationUnit cu : sourceList) {
monitor.worked(1);
requestor.acceptSource(cu);
if (monitor.isCanceled())
throw new OperationCanceledException();
}
fParseList= requestor.getUndoneElements();
fIndex= cuMonitor.getIndex();
} finally {
}
}
public void dispose() {
for (ICompilationUnit cu : fWorkingCopies.values()) {
try {
cu.discardWorkingCopy();
} catch (JavaModelException e) {
JavaPlugin.log(e);
}
}
fWorkingCopies.clear();
}
private boolean requiresAST(ICleanUp[] cleanUps) {
for (ICleanUp cleanUp : cleanUps) {
if (cleanUp.getRequirements().requiresAST()) {
return true;
}
}
return false;
}
public Change[] getResult() {
Change[] result= new Change[fSolutions.size()];
int i=0;
for (Entry<ICompilationUnit, List<CleanUpChange>> entry : fSolutions.entrySet()) {
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 (CleanUpChange change : changes) {
mscuc.addChange(createGroupFreeChange(change));
}
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 (CleanUpChange change : changes) {
TextEdit edit= change.getEdit().copy();
try {
edit.apply(document, TextEdit.UPDATE_REGIONS);
} catch (MalformedTreeException | 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<>();
fProjects= new Hashtable<>();
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<>();
for (List<CleanUpTarget> projectTargets : fProjects.values()) {
result.addAll(projectTargets);
}
return result.toArray(new CleanUpTarget[result.size()]);
}
public int getCleanUpTargetsSize() {
int result= 0;
for (List<CleanUpTarget> projectTargets : fProjects.values()) {
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.size()]);
}
public void setLeaveFilesDirty(boolean leaveFilesDirty) {
fLeaveFilesDirty= leaveFilesDirty;
}
@Override
public String getName() {
return fName;
}
@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();
}
@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;
}
@Override
public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException {
if (pm == null)
pm= new NullProgressMonitor();
if (fProjects.isEmpty() || fCleanUps.isEmpty()) {
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 (Entry<IJavaProject, List<CleanUpTarget>> entry : fProjects.entrySet()) {
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 (Change c : changes) {
change.add(c);
}
}
fChange= change;
List<IResource> files= new ArrayList<>();
findFilesToBeModified(change, files);
result.merge(Checks.validateModifiesFiles(files.toArray(new IFile[files.size()]), getValidationContext(), pm));
} finally {
pm.done();
}
return result;
}
private void findFilesToBeModified(CompositeChange change, List<IResource> result) throws JavaModelException {
for (Change child : change.getChildren()) {
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 (ICleanUp cleanUp : cleanUps) {
cleanUp.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 (ICleanUp cleanUp : cleanUps) {
result.merge(cleanUp.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 (ICleanUp cleanUp : cleanUps) {
result.merge(cleanUp.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) {
for (TextEditBasedChangeGroup changeGroup : source.getChangeGroups()) {
TextEditGroup textEditGroup= changeGroup.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());
}
for (TextEdit textEdit : textEditGroup.getTextEdits()) {
newGroup.addTextEdit(textEdit);
}
target.addTextEditGroup(newGroup);
}
}
@Override
protected RefactoringTickProvider doGetRefactoringTickProvider() {
return CLEAN_UP_REFACTORING_TICK_PROVIDER;
}
@Override
public ISchedulingRule getSchedulingRule() {
return ResourcesPlugin.getWorkspace().getRoot();
}
public static ASTParser createCleanUpASTParser() {
ASTParser result= ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL);
result.setResolveBindings(true);
result.setStatementsRecovery(IASTSharedValues.SHARED_AST_STATEMENT_RECOVERY);
result.setBindingsRecovery(IASTSharedValues.SHARED_BINDING_RECOVERY);
return result;
}
}