0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.jdt.internal.corext.fix;
0011:
0012: import java.util.ArrayList;
0013: import java.util.Hashtable;
0014: import java.util.Iterator;
0015: import java.util.List;
0016: import java.util.Map;
0017: import java.util.Map.Entry;
0018:
0019: import org.eclipse.text.edits.MultiTextEdit;
0020: import org.eclipse.text.edits.TextEdit;
0021: import org.eclipse.text.edits.TextEditGroup;
0022: import org.eclipse.text.edits.TextEditVisitor;
0023: import org.eclipse.text.edits.UndoEdit;
0024:
0025: import org.eclipse.core.runtime.CoreException;
0026: import org.eclipse.core.runtime.IProgressMonitor;
0027: import org.eclipse.core.runtime.NullProgressMonitor;
0028: import org.eclipse.core.runtime.OperationCanceledException;
0029: import org.eclipse.core.runtime.SubProgressMonitor;
0030: import org.eclipse.core.runtime.jobs.ISchedulingRule;
0031:
0032: import org.eclipse.core.resources.IFile;
0033: import org.eclipse.core.resources.ProjectScope;
0034: import org.eclipse.core.resources.ResourcesPlugin;
0035:
0036: import org.eclipse.ltk.core.refactoring.CategorizedTextEditGroup;
0037: import org.eclipse.ltk.core.refactoring.Change;
0038: import org.eclipse.ltk.core.refactoring.CompositeChange;
0039: import org.eclipse.ltk.core.refactoring.ContentStamp;
0040: import org.eclipse.ltk.core.refactoring.GroupCategory;
0041: import org.eclipse.ltk.core.refactoring.GroupCategorySet;
0042: import org.eclipse.ltk.core.refactoring.NullChange;
0043: import org.eclipse.ltk.core.refactoring.Refactoring;
0044: import org.eclipse.ltk.core.refactoring.RefactoringStatus;
0045: import org.eclipse.ltk.core.refactoring.RefactoringTickProvider;
0046: import org.eclipse.ltk.core.refactoring.TextChange;
0047: import org.eclipse.ltk.core.refactoring.TextEditBasedChangeGroup;
0048: import org.eclipse.ltk.core.refactoring.TextFileChange;
0049:
0050: import org.eclipse.jdt.core.ICompilationUnit;
0051: import org.eclipse.jdt.core.IJavaProject;
0052: import org.eclipse.jdt.core.IPackageFragment;
0053: import org.eclipse.jdt.core.JavaModelException;
0054: import org.eclipse.jdt.core.WorkingCopyOwner;
0055: import org.eclipse.jdt.core.dom.ASTParser;
0056: import org.eclipse.jdt.core.dom.ASTRequestor;
0057: import org.eclipse.jdt.core.dom.CompilationUnit;
0058:
0059: import org.eclipse.jdt.internal.corext.dom.ASTBatchParser;
0060: import org.eclipse.jdt.internal.corext.refactoring.Checks;
0061: import org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange;
0062: import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationStateChange;
0063: import org.eclipse.jdt.internal.corext.refactoring.changes.MultiStateCompilationUnitChange;
0064: import org.eclipse.jdt.internal.corext.refactoring.changes.TextChangeCompatibility;
0065: import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
0066: import org.eclipse.jdt.internal.corext.util.Messages;
0067:
0068: import org.eclipse.jdt.ui.JavaElementLabels;
0069: import org.eclipse.jdt.ui.text.java.IProblemLocation;
0070:
0071: import org.eclipse.jdt.internal.ui.JavaPlugin;
0072: import org.eclipse.jdt.internal.ui.fix.CleanUpOptions;
0073: import org.eclipse.jdt.internal.ui.fix.CodeFormatCleanUp;
0074: import org.eclipse.jdt.internal.ui.fix.CodeStyleCleanUp;
0075: import org.eclipse.jdt.internal.ui.fix.CommentFormatCleanUp;
0076: import org.eclipse.jdt.internal.ui.fix.ControlStatementsCleanUp;
0077: import org.eclipse.jdt.internal.ui.fix.ConvertLoopCleanUp;
0078: import org.eclipse.jdt.internal.ui.fix.ExpressionsCleanUp;
0079: import org.eclipse.jdt.internal.ui.fix.ICleanUp;
0080: import org.eclipse.jdt.internal.ui.fix.ImportsCleanUp;
0081: import org.eclipse.jdt.internal.ui.fix.Java50CleanUp;
0082: import org.eclipse.jdt.internal.ui.fix.PotentialProgrammingProblemsCleanUp;
0083: import org.eclipse.jdt.internal.ui.fix.SortMembersCleanUp;
0084: import org.eclipse.jdt.internal.ui.fix.StringCleanUp;
0085: import org.eclipse.jdt.internal.ui.fix.UnimplementedCodeCleanUp;
0086: import org.eclipse.jdt.internal.ui.fix.UnnecessaryCodeCleanUp;
0087: import org.eclipse.jdt.internal.ui.fix.UnusedCodeCleanUp;
0088: import org.eclipse.jdt.internal.ui.fix.VariableDeclarationCleanUp;
0089: import org.eclipse.jdt.internal.ui.fix.ICleanUp.CleanUpContext;
0090: import org.eclipse.jdt.internal.ui.fix.IMultiFix.MultiFixContext;
0091: import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
0092: import org.eclipse.jdt.internal.ui.refactoring.IScheduledRefactoring;
0093:
0094: public class CleanUpRefactoring extends Refactoring implements
0095: IScheduledRefactoring {
0096:
0097: public static class CleanUpTarget {
0098:
0099: private final ICompilationUnit fCompilationUnit;
0100:
0101: public CleanUpTarget(ICompilationUnit unit) {
0102: fCompilationUnit = unit;
0103: }
0104:
0105: public ICompilationUnit getCompilationUnit() {
0106: return fCompilationUnit;
0107: }
0108: }
0109:
0110: public static class MultiFixTarget extends CleanUpTarget {
0111:
0112: private final IProblemLocation[] fProblems;
0113:
0114: public MultiFixTarget(ICompilationUnit unit,
0115: IProblemLocation[] problems) {
0116: super (unit);
0117: fProblems = problems;
0118: }
0119:
0120: public IProblemLocation[] getProblems() {
0121: return fProblems;
0122: }
0123: }
0124:
0125: public static class CleanUpChange extends CompilationUnitChange {
0126:
0127: private UndoEdit fUndoEdit;
0128:
0129: public CleanUpChange(String name, ICompilationUnit cunit) {
0130: super (name, cunit);
0131: }
0132:
0133: /**
0134: * {@inheritDoc}
0135: */
0136: protected Change createUndoChange(UndoEdit edit,
0137: ContentStamp stampToRestore) {
0138: fUndoEdit = edit;
0139: return super .createUndoChange(edit, stampToRestore);
0140: }
0141:
0142: public UndoEdit getUndoEdit() {
0143: return fUndoEdit;
0144: }
0145: }
0146:
0147: private static class FixCalculationException extends
0148: RuntimeException {
0149:
0150: private static final long serialVersionUID = 3807273310144726165L;
0151:
0152: private final CoreException fException;
0153:
0154: public FixCalculationException(CoreException exception) {
0155: fException = exception;
0156: }
0157:
0158: public CoreException getException() {
0159: return fException;
0160: }
0161: }
0162:
0163: private static class ParseListElement {
0164:
0165: private final CleanUpTarget fTarget;
0166: private final ICleanUp[] fCleanUpsArray;
0167:
0168: public ParseListElement(CleanUpTarget cleanUpTarget,
0169: ICleanUp[] cleanUps) {
0170: fTarget = cleanUpTarget;
0171: fCleanUpsArray = cleanUps;
0172: }
0173:
0174: public CleanUpTarget getTarget() {
0175: return fTarget;
0176: }
0177:
0178: public ICleanUp[] getCleanUps() {
0179: return fCleanUpsArray;
0180: }
0181: }
0182:
0183: private final class CleanUpRefactoringProgressMonitor extends
0184: SubProgressMonitor {
0185:
0186: private double fRealWork;
0187: private int fFlushCount;
0188: private final int fSize;
0189: private final int fIndex;
0190:
0191: private CleanUpRefactoringProgressMonitor(
0192: IProgressMonitor monitor, int ticks, int size, int index) {
0193: super (monitor, ticks);
0194: fFlushCount = 0;
0195: fSize = size;
0196: fIndex = index;
0197: }
0198:
0199: /**
0200: * {@inheritDoc}
0201: */
0202: public void internalWorked(double work) {
0203: fRealWork += work;
0204: }
0205:
0206: public void flush() {
0207: super .internalWorked(fRealWork);
0208: reset();
0209: fFlushCount++;
0210: }
0211:
0212: public void reset() {
0213: fRealWork = 0.0;
0214: }
0215:
0216: public void done() {
0217: }
0218:
0219: public int getIndex() {
0220: return fIndex + fFlushCount;
0221: }
0222:
0223: public String getSubTaskMessage(ICompilationUnit source) {
0224: String typeName = source.getElementName();
0225: return Messages
0226: .format(
0227: FixMessages.CleanUpRefactoring_ProcessingCompilationUnit_message,
0228: new Object[] { new Integer(getIndex()),
0229: new Integer(fSize), typeName });
0230: }
0231: }
0232:
0233: private static class CleanUpASTRequestor extends ASTRequestor {
0234:
0235: private final List/*<ParseListElement>*/fUndoneElements;
0236: private final Hashtable/*<ICompilationUnit, Change>*/fSolutions;
0237: private final Hashtable/*<ICompilationUnit, ParseListElement>*/fCompilationUnitParseElementMap;
0238: private final CleanUpRefactoringProgressMonitor fMonitor;
0239:
0240: public CleanUpASTRequestor(List parseList, Hashtable solutions,
0241: CleanUpRefactoringProgressMonitor monitor) {
0242: fSolutions = solutions;
0243: fMonitor = monitor;
0244: fUndoneElements = new ArrayList();
0245: fCompilationUnitParseElementMap = new Hashtable(parseList
0246: .size());
0247: for (Iterator iter = parseList.iterator(); iter.hasNext();) {
0248: ParseListElement element = (ParseListElement) iter
0249: .next();
0250: fCompilationUnitParseElementMap.put(element.getTarget()
0251: .getCompilationUnit(), element);
0252: }
0253: }
0254:
0255: /**
0256: * {@inheritDoc}
0257: */
0258: public void acceptAST(ICompilationUnit source,
0259: CompilationUnit ast) {
0260:
0261: fMonitor.subTask(fMonitor.getSubTaskMessage(source));
0262:
0263: ICompilationUnit primary = (ICompilationUnit) source
0264: .getPrimaryElement();
0265: ParseListElement element = (ParseListElement) fCompilationUnitParseElementMap
0266: .get(primary);
0267: CleanUpTarget target = element.getTarget();
0268:
0269: CleanUpContext context;
0270: if (target instanceof MultiFixTarget) {
0271: context = new MultiFixContext(source, ast,
0272: ((MultiFixTarget) target).getProblems());
0273: } else {
0274: context = new CleanUpContext(source, ast);
0275: }
0276: ICleanUp[] rejectedCleanUps = calculateSolutions(context,
0277: element.getCleanUps());
0278:
0279: if (rejectedCleanUps.length > 0) {
0280: fUndoneElements.add(new ParseListElement(target,
0281: rejectedCleanUps));
0282: fMonitor.reset();
0283: } else {
0284: fMonitor.flush();
0285: }
0286: }
0287:
0288: public void acceptSource(ICompilationUnit source) {
0289: acceptAST(source, null);
0290: }
0291:
0292: public List getUndoneElements() {
0293: return fUndoneElements;
0294: }
0295:
0296: private ICleanUp[] calculateSolutions(CleanUpContext context,
0297: ICleanUp[] cleanUps) {
0298: List/*<ICleanUp>*/result = new ArrayList();
0299: CleanUpChange solution;
0300: try {
0301: solution = calculateChange(context, cleanUps, result);
0302: } catch (CoreException e) {
0303: throw new FixCalculationException(e);
0304: }
0305:
0306: if (solution != null) {
0307: try {
0308: integrateSolution(solution, context
0309: .getCompilationUnit());
0310: } catch (JavaModelException e) {
0311: throw new FixCalculationException(e);
0312: }
0313: }
0314:
0315: return (ICleanUp[]) result.toArray(new ICleanUp[result
0316: .size()]);
0317: }
0318:
0319: private void integrateSolution(CleanUpChange solution,
0320: ICompilationUnit source) throws JavaModelException {
0321: ICompilationUnit primary = source.getPrimary();
0322:
0323: List changes = (List) fSolutions.get(primary);
0324: if (changes == null) {
0325: changes = new ArrayList();
0326: fSolutions.put(primary, changes);
0327: }
0328: changes.add(solution);
0329: }
0330: }
0331:
0332: private class CleanUpFixpointIterator {
0333:
0334: private List/*<ParseListElement>*/fParseList;
0335: private final Hashtable/*<ICompilationUnit, List<CleanUpChange>>*/fSolutions;
0336: private final Hashtable/*<ICompilationUnit (primary), ICompilationUnit (working copy)>*/fWorkingCopies;
0337: private final Map fCleanUpOptions;
0338: private final int fSize;
0339: private int fIndex;
0340:
0341: public CleanUpFixpointIterator(CleanUpTarget[] targets,
0342: ICleanUp[] cleanUps) {
0343: fSolutions = new Hashtable(targets.length);
0344: fWorkingCopies = new Hashtable();
0345:
0346: fParseList = new ArrayList(targets.length);
0347: for (int i = 0; i < targets.length; i++) {
0348: fParseList.add(new ParseListElement(targets[i],
0349: cleanUps));
0350: }
0351:
0352: fCleanUpOptions = new Hashtable();
0353: for (int i = 0; i < cleanUps.length; i++) {
0354: ICleanUp cleanUp = cleanUps[i];
0355: Map currentCleanUpOption = cleanUp.getRequirements()
0356: .getCompilerOptions();
0357: if (currentCleanUpOption != null)
0358: fCleanUpOptions.putAll(currentCleanUpOption);
0359: }
0360:
0361: fSize = targets.length;
0362: fIndex = 1;
0363: }
0364:
0365: public boolean hasNext() {
0366: return !fParseList.isEmpty();
0367: }
0368:
0369: public void next(IProgressMonitor monitor) throws CoreException {
0370: List parseList = new ArrayList();
0371: List sourceList = new ArrayList();
0372:
0373: try {
0374: for (Iterator iter = fParseList.iterator(); iter
0375: .hasNext();) {
0376: ParseListElement element = (ParseListElement) iter
0377: .next();
0378:
0379: ICompilationUnit compilationUnit = element
0380: .getTarget().getCompilationUnit();
0381: if (fSolutions.containsKey(compilationUnit)) {
0382: if (fWorkingCopies.containsKey(compilationUnit)) {
0383: compilationUnit = (ICompilationUnit) fWorkingCopies
0384: .get(compilationUnit);
0385: } else {
0386: compilationUnit = compilationUnit
0387: .getWorkingCopy(
0388: new WorkingCopyOwner() {
0389: }, null);
0390: fWorkingCopies.put(compilationUnit
0391: .getPrimary(), compilationUnit);
0392: }
0393: applyChange(compilationUnit, (List) fSolutions
0394: .get(compilationUnit.getPrimary()));
0395: }
0396:
0397: if (requiresAST(element.getCleanUps())) {
0398: parseList.add(compilationUnit);
0399: } else {
0400: sourceList.add(compilationUnit);
0401: }
0402: }
0403:
0404: CleanUpRefactoringProgressMonitor cuMonitor = new CleanUpRefactoringProgressMonitor(
0405: monitor, parseList.size() + sourceList.size(),
0406: fSize, fIndex);
0407: CleanUpASTRequestor requestor = new CleanUpASTRequestor(
0408: fParseList, fSolutions, cuMonitor);
0409: if (parseList.size() > 0) {
0410: ASTBatchParser parser = new ASTBatchParser() {
0411: protected ASTParser createParser(
0412: IJavaProject project) {
0413: ASTParser result = ASTParser
0414: .newParser(ASTProvider.SHARED_AST_LEVEL);
0415: result.setResolveBindings(true);
0416: result.setProject(project);
0417:
0418: Map options = RefactoringASTParser
0419: .getCompilerOptions(project);
0420: options.putAll(fCleanUpOptions);
0421: result.setCompilerOptions(options);
0422: return result;
0423: }
0424: };
0425: try {
0426: ICompilationUnit[] units = (ICompilationUnit[]) parseList
0427: .toArray(new ICompilationUnit[parseList
0428: .size()]);
0429: parser.createASTs(units, new String[0],
0430: requestor, cuMonitor);
0431: } catch (FixCalculationException e) {
0432: throw e.getException();
0433: }
0434: }
0435:
0436: for (Iterator iterator = sourceList.iterator(); iterator
0437: .hasNext();) {
0438: ICompilationUnit cu = (ICompilationUnit) iterator
0439: .next();
0440:
0441: monitor.worked(1);
0442:
0443: requestor.acceptSource(cu);
0444:
0445: if (monitor.isCanceled())
0446: throw new OperationCanceledException();
0447: }
0448:
0449: fParseList = requestor.getUndoneElements();
0450: fIndex = cuMonitor.getIndex();
0451: } finally {
0452: }
0453: }
0454:
0455: public void dispose() {
0456: for (Iterator iterator = fWorkingCopies.values().iterator(); iterator
0457: .hasNext();) {
0458: ICompilationUnit cu = (ICompilationUnit) iterator
0459: .next();
0460: try {
0461: cu.discardWorkingCopy();
0462: } catch (JavaModelException e) {
0463: JavaPlugin.log(e);
0464: }
0465: }
0466: }
0467:
0468: private boolean requiresAST(ICleanUp[] cleanUps)
0469: throws CoreException {
0470: for (int i = 0; i < cleanUps.length; i++) {
0471: if (cleanUps[i].getRequirements().requiresAST())
0472: return true;
0473: }
0474: return false;
0475: }
0476:
0477: public Change[] getResult() {
0478:
0479: Change[] result = new Change[fSolutions.size()];
0480: int i = 0;
0481: for (Iterator iterator = fSolutions.entrySet().iterator(); iterator
0482: .hasNext();) {
0483: Map.Entry entry = (Map.Entry) iterator.next();
0484:
0485: List changes = (List) entry.getValue();
0486: ICompilationUnit unit = (ICompilationUnit) entry
0487: .getKey();
0488:
0489: int saveMode;
0490: try {
0491: if (fLeaveFilesDirty
0492: || unit.getBuffer().hasUnsavedChanges()) {
0493: saveMode = TextFileChange.LEAVE_DIRTY;
0494: } else {
0495: saveMode = TextFileChange.FORCE_SAVE;
0496: }
0497: } catch (JavaModelException e) {
0498: saveMode = TextFileChange.LEAVE_DIRTY;
0499: JavaPlugin.log(e);
0500: }
0501:
0502: if (changes.size() == 1) {
0503: CleanUpChange change = (CleanUpChange) changes
0504: .get(0);
0505: change.setSaveMode(saveMode);
0506: result[i] = change;
0507: } else {
0508: MultiStateCompilationUnitChange mscuc = new MultiStateCompilationUnitChange(
0509: getChangeName(unit), unit);
0510: for (int j = 0; j < changes.size(); j++) {
0511: mscuc
0512: .addChange(createGroupFreeChange((CleanUpChange) changes
0513: .get(j)));
0514: }
0515: mscuc.setSaveMode(saveMode);
0516: result[i] = mscuc;
0517: }
0518:
0519: i++;
0520: }
0521:
0522: return result;
0523: }
0524:
0525: private TextChange createGroupFreeChange(CleanUpChange change) {
0526: CleanUpChange result = new CleanUpChange(change.getName(),
0527: change.getCompilationUnit());
0528: result.setEdit(change.getEdit());
0529: result.setSaveMode(change.getSaveMode());
0530: return result;
0531: }
0532:
0533: private void applyChange(ICompilationUnit compilationUnit,
0534: List changes) throws JavaModelException, CoreException {
0535: if (changes.size() == 1) {
0536: CleanUpChange change = (CleanUpChange) changes
0537: .get(changes.size() - 1);
0538: compilationUnit.getBuffer().setContents(
0539: change.getPreviewContent(null));
0540: } else {
0541: MultiStateCompilationUnitChange mscuc = new MultiStateCompilationUnitChange(
0542: "", compilationUnit.getPrimary()); //$NON-NLS-1$
0543: for (int i = 0; i < changes.size(); i++) {
0544: mscuc.addChange((CleanUpChange) changes.get(i));
0545: }
0546: compilationUnit.getBuffer().setContents(
0547: mscuc.getPreviewContent(null));
0548: }
0549: }
0550: }
0551:
0552: private static final RefactoringTickProvider CLEAN_UP_REFACTORING_TICK_PROVIDER = new RefactoringTickProvider(
0553: 0, 1, 0, 0);
0554:
0555: private final List/*<ICleanUp>*/fCleanUps;
0556: private final Hashtable/*<IJavaProject, List<CleanUpTarget>*/fProjects;
0557: private Change fChange;
0558: private boolean fLeaveFilesDirty;
0559: private final String fName;
0560:
0561: public CleanUpRefactoring() {
0562: this (FixMessages.CleanUpRefactoring_Refactoring_name);
0563: }
0564:
0565: public CleanUpRefactoring(String name) {
0566: fName = name;
0567: fCleanUps = new ArrayList();
0568: fProjects = new Hashtable();
0569: }
0570:
0571: public void addCompilationUnit(ICompilationUnit unit) {
0572: addCleanUpTarget(new CleanUpTarget(unit));
0573: }
0574:
0575: public void addCleanUpTarget(CleanUpTarget target) {
0576:
0577: IJavaProject javaProject = target.getCompilationUnit()
0578: .getJavaProject();
0579: if (!fProjects.containsKey(javaProject))
0580: fProjects.put(javaProject, new ArrayList());
0581:
0582: List targets = (List) fProjects.get(javaProject);
0583: targets.add(target);
0584: }
0585:
0586: public CleanUpTarget[] getCleanUpTargets() {
0587: List result = new ArrayList();
0588: for (Iterator iter = fProjects.values().iterator(); iter
0589: .hasNext();) {
0590: List projectTargets = (List) iter.next();
0591: result.addAll(projectTargets);
0592: }
0593: return (CleanUpTarget[]) result
0594: .toArray(new CleanUpTarget[result.size()]);
0595: }
0596:
0597: public int getCleanUpTargetsSize() {
0598: int result = 0;
0599: for (Iterator iter = fProjects.values().iterator(); iter
0600: .hasNext();) {
0601: List projectTargets = (List) iter.next();
0602: result += projectTargets.size();
0603: }
0604: return result;
0605: }
0606:
0607: public void addCleanUp(ICleanUp fix) {
0608: fCleanUps.add(fix);
0609: }
0610:
0611: public void clearCleanUps() {
0612: fCleanUps.clear();
0613: }
0614:
0615: public boolean hasCleanUps() {
0616: return !fCleanUps.isEmpty();
0617: }
0618:
0619: public ICleanUp[] getCleanUps() {
0620: return (ICleanUp[]) fCleanUps.toArray(new ICleanUp[fCleanUps
0621: .size()]);
0622: }
0623:
0624: public IJavaProject[] getProjects() {
0625: return (IJavaProject[]) fProjects.keySet().toArray(
0626: new IJavaProject[fProjects.keySet().size()]);
0627: }
0628:
0629: public void setLeaveFilesDirty(boolean leaveFilesDirty) {
0630: fLeaveFilesDirty = leaveFilesDirty;
0631: }
0632:
0633: /* (non-Javadoc)
0634: * @see org.eclipse.ltk.core.refactoring.Refactoring#getName()
0635: */
0636: public String getName() {
0637: return fName;
0638: }
0639:
0640: /* (non-Javadoc)
0641: * @see org.eclipse.ltk.core.refactoring.Refactoring#checkInitialConditions(org.eclipse.core.runtime.IProgressMonitor)
0642: */
0643: public RefactoringStatus checkInitialConditions(IProgressMonitor pm)
0644: throws CoreException, OperationCanceledException {
0645: if (pm != null) {
0646: pm.beginTask("", 1); //$NON-NLS-1$
0647: pm.worked(1);
0648: pm.done();
0649: }
0650: return new RefactoringStatus();
0651: }
0652:
0653: /* (non-Javadoc)
0654: * @see org.eclipse.ltk.core.refactoring.Refactoring#createChange(org.eclipse.core.runtime.IProgressMonitor)
0655: */
0656: public Change createChange(IProgressMonitor pm)
0657: throws CoreException, OperationCanceledException {
0658: if (pm != null) {
0659: pm.beginTask("", 1); //$NON-NLS-1$
0660: pm.worked(1);
0661: pm.done();
0662: }
0663: return fChange;
0664: }
0665:
0666: /* (non-Javadoc)
0667: * @see org.eclipse.ltk.core.refactoring.Refactoring#checkFinalConditions(org.eclipse.core.runtime.IProgressMonitor)
0668: */
0669: public RefactoringStatus checkFinalConditions(IProgressMonitor pm)
0670: throws CoreException, OperationCanceledException {
0671:
0672: if (pm == null)
0673: pm = new NullProgressMonitor();
0674:
0675: if (fProjects.size() == 0 || fCleanUps.size() == 0) {
0676: pm.beginTask("", 1); //$NON-NLS-1$
0677: pm.worked(1);
0678: pm.done();
0679: fChange = new NullChange();
0680:
0681: return new RefactoringStatus();
0682: }
0683:
0684: int cuCount = getCleanUpTargetsSize();
0685:
0686: ICleanUp first = (ICleanUp) fCleanUps.get(0);
0687: boolean usesProjectSettings = first.getOptions() == null;
0688:
0689: RefactoringStatus result = new RefactoringStatus();
0690:
0691: ICleanUp[] cleanUps = getCleanUps();
0692: pm
0693: .beginTask(
0694: "", cuCount * 2 * fCleanUps.size() + 4 * cleanUps.length); //$NON-NLS-1$
0695: try {
0696: DynamicValidationStateChange change = new DynamicValidationStateChange(
0697: getName());
0698: change.setSchedulingRule(getSchedulingRule());
0699: for (Iterator projectIter = fProjects.entrySet().iterator(); projectIter
0700: .hasNext();) {
0701: Map.Entry entry = (Entry) projectIter.next();
0702: IJavaProject project = (IJavaProject) entry.getKey();
0703: List targetsList = (List) entry.getValue();
0704: CleanUpTarget[] targets = (CleanUpTarget[]) targetsList
0705: .toArray(new CleanUpTarget[targetsList.size()]);
0706:
0707: if (usesProjectSettings) {
0708: result.merge(setProjectOptions(project, cleanUps));
0709: if (result.hasFatalError())
0710: return result;
0711: }
0712:
0713: result
0714: .merge(checkPreConditions(project, targets,
0715: new SubProgressMonitor(pm,
0716: 3 * cleanUps.length)));
0717: if (result.hasFatalError())
0718: return result;
0719:
0720: Change[] changes = cleanUpProject(project, targets,
0721: cleanUps, pm);
0722:
0723: result
0724: .merge(checkPostConditions(new SubProgressMonitor(
0725: pm, cleanUps.length)));
0726: if (result.hasFatalError())
0727: return result;
0728:
0729: for (int i = 0; i < changes.length; i++) {
0730: change.add(changes[i]);
0731: }
0732: }
0733: fChange = change;
0734:
0735: List files = new ArrayList();
0736: findFilesToBeModified(change, files);
0737: result.merge(Checks.validateModifiesFiles((IFile[]) files
0738: .toArray(new IFile[files.size()]),
0739: getValidationContext()));
0740: if (result.hasFatalError())
0741: return result;
0742: } finally {
0743: pm.done();
0744: if (usesProjectSettings) {
0745: for (int i = 0; i < cleanUps.length; i++) {
0746: cleanUps[i].setOptions(null);
0747: }
0748: }
0749: }
0750:
0751: return result;
0752: }
0753:
0754: private void findFilesToBeModified(CompositeChange change,
0755: List result) throws JavaModelException {
0756: Change[] children = change.getChildren();
0757: for (int i = 0; i < children.length; i++) {
0758: Change child = children[i];
0759: if (child instanceof CompositeChange) {
0760: findFilesToBeModified((CompositeChange) child, result);
0761: } else if (child instanceof MultiStateCompilationUnitChange) {
0762: result.add(((MultiStateCompilationUnitChange) child)
0763: .getCompilationUnit()
0764: .getCorrespondingResource());
0765: } else if (child instanceof CompilationUnitChange) {
0766: result.add(((CompilationUnitChange) child)
0767: .getCompilationUnit()
0768: .getCorrespondingResource());
0769: }
0770: }
0771: }
0772:
0773: private Change[] cleanUpProject(IJavaProject project,
0774: CleanUpTarget[] targets, ICleanUp[] cleanUps,
0775: IProgressMonitor monitor) throws CoreException {
0776: CleanUpFixpointIterator iter = new CleanUpFixpointIterator(
0777: targets, cleanUps);
0778:
0779: SubProgressMonitor subMonitor = new SubProgressMonitor(monitor,
0780: 2 * targets.length * cleanUps.length);
0781: subMonitor.beginTask("", targets.length); //$NON-NLS-1$
0782: subMonitor.subTask(Messages.format(
0783: FixMessages.CleanUpRefactoring_Parser_Startup_message,
0784: project.getElementName()));
0785: try {
0786: while (iter.hasNext()) {
0787: iter.next(subMonitor);
0788: }
0789:
0790: return iter.getResult();
0791: } finally {
0792: iter.dispose();
0793: subMonitor.done();
0794: }
0795: }
0796:
0797: private RefactoringStatus setProjectOptions(
0798: IJavaProject javaProject, ICleanUp[] cleanUps)
0799: throws CoreException {
0800: Map options = CleanUpPreferenceUtil
0801: .loadOptions(new ProjectScope(javaProject.getProject()));
0802: if (options == null) {
0803: return RefactoringStatus
0804: .createFatalErrorStatus(Messages
0805: .format(
0806: FixMessages.CleanUpRefactoring_could_not_retrive_profile,
0807: javaProject.getElementName()));
0808: }
0809:
0810: CleanUpOptions cleanUpOptions = new CleanUpOptions(options);
0811: for (int j = 0; j < cleanUps.length; j++) {
0812: cleanUps[j].setOptions(cleanUpOptions);
0813: }
0814:
0815: return new RefactoringStatus();
0816: }
0817:
0818: private RefactoringStatus checkPreConditions(
0819: IJavaProject javaProject, CleanUpTarget[] targets,
0820: IProgressMonitor monitor) throws CoreException {
0821: RefactoringStatus result = new RefactoringStatus();
0822:
0823: ICompilationUnit[] compilationUnits = new ICompilationUnit[targets.length];
0824: for (int i = 0; i < targets.length; i++) {
0825: compilationUnits[i] = targets[i].getCompilationUnit();
0826: }
0827:
0828: ICleanUp[] cleanUps = getCleanUps();
0829: monitor
0830: .beginTask(
0831: "", compilationUnits.length * cleanUps.length); //$NON-NLS-1$
0832: monitor.subTask(Messages.format(
0833: FixMessages.CleanUpRefactoring_Initialize_message,
0834: javaProject.getElementName()));
0835: try {
0836: for (int j = 0; j < cleanUps.length; j++) {
0837: result.merge(cleanUps[j].checkPreConditions(
0838: javaProject, compilationUnits,
0839: new SubProgressMonitor(monitor,
0840: compilationUnits.length)));
0841: if (result.hasFatalError())
0842: return result;
0843: }
0844: } finally {
0845: monitor.done();
0846: }
0847:
0848: return result;
0849: }
0850:
0851: private RefactoringStatus checkPostConditions(
0852: SubProgressMonitor monitor) throws CoreException {
0853: RefactoringStatus result = new RefactoringStatus();
0854:
0855: ICleanUp[] cleanUps = getCleanUps();
0856: monitor.beginTask("", cleanUps.length); //$NON-NLS-1$
0857: monitor
0858: .subTask(FixMessages.CleanUpRefactoring_checkingPostConditions_message);
0859: try {
0860: for (int j = 0; j < cleanUps.length; j++) {
0861: result.merge(cleanUps[j]
0862: .checkPostConditions(new SubProgressMonitor(
0863: monitor, 1)));
0864: if (result.hasFatalError())
0865: return result;
0866: }
0867: } finally {
0868: monitor.done();
0869: }
0870: return result;
0871: }
0872:
0873: private static String getChangeName(ICompilationUnit compilationUnit) {
0874: StringBuffer buf = new StringBuffer();
0875: JavaElementLabels.getCompilationUnitLabel(compilationUnit,
0876: JavaElementLabels.ALL_DEFAULT, buf);
0877: buf.append(JavaElementLabels.CONCAT_STRING);
0878:
0879: StringBuffer buf2 = new StringBuffer();
0880: JavaElementLabels.getPackageFragmentLabel(
0881: (IPackageFragment) compilationUnit.getParent(),
0882: JavaElementLabels.P_QUALIFIED, buf2);
0883: buf.append(buf2.toString().replace('.', '/'));
0884:
0885: return buf.toString();
0886: }
0887:
0888: public static CleanUpChange calculateChange(CleanUpContext context,
0889: ICleanUp[] cleanUps, List undoneCleanUps)
0890: throws CoreException {
0891: if (cleanUps.length == 0)
0892: return null;
0893:
0894: CleanUpChange solution = null;
0895: int i = 0;
0896: do {
0897: ICleanUp cleanUp = cleanUps[i];
0898: IFix fix = cleanUp.createFix(context);
0899: if (fix != null) {
0900: CompilationUnitChange current = fix.createChange();
0901: TextEdit currentEdit = pack(current.getEdit());
0902:
0903: if (solution != null) {
0904: if (intersects(currentEdit, solution.getEdit())) {
0905: undoneCleanUps.add(cleanUp);
0906: } else {
0907: CleanUpChange merge = new CleanUpChange(
0908: FixMessages.CleanUpRefactoring_clean_up_multi_chang_name,
0909: context.getCompilationUnit());
0910: merge.setEdit(merge(currentEdit, solution
0911: .getEdit()));
0912:
0913: copyChangeGroups(merge, solution);
0914: copyChangeGroups(merge, current);
0915:
0916: solution = merge;
0917: }
0918: } else {
0919: solution = new CleanUpChange(current.getName(),
0920: context.getCompilationUnit());
0921: solution.setEdit(currentEdit);
0922:
0923: copyChangeGroups(solution, current);
0924: }
0925: }
0926: i++;
0927: } while (i < cleanUps.length
0928: && (context.getAST() == null || !cleanUps[i]
0929: .getRequirements().requiresFreshAST()));
0930:
0931: for (; i < cleanUps.length; i++) {
0932: undoneCleanUps.add(cleanUps[i]);
0933: }
0934: return solution;
0935: }
0936:
0937: private static void copyChangeGroups(CompilationUnitChange target,
0938: CompilationUnitChange source) {
0939: TextEditBasedChangeGroup[] changeGroups = source
0940: .getChangeGroups();
0941: for (int i = 0; i < changeGroups.length; i++) {
0942: TextEditGroup textEditGroup = changeGroups[i]
0943: .getTextEditGroup();
0944: TextEditGroup newGroup;
0945: if (textEditGroup instanceof CategorizedTextEditGroup) {
0946: String label = textEditGroup.getName();
0947: newGroup = new CategorizedTextEditGroup(label,
0948: new GroupCategorySet(new GroupCategory(label,
0949: label, label)));
0950: } else {
0951: newGroup = new TextEditGroup(textEditGroup.getName());
0952: }
0953: TextEdit[] textEdits = textEditGroup.getTextEdits();
0954: for (int j = 0; j < textEdits.length; j++) {
0955: newGroup.addTextEdit(textEdits[j]);
0956: }
0957: target.addTextEditGroup(newGroup);
0958: }
0959: }
0960:
0961: private static TextEdit pack(TextEdit edit) {
0962: if (!edit.hasChildren())
0963: return edit;
0964:
0965: final List edits = new ArrayList();
0966: edit.accept(new TextEditVisitor() {
0967: public boolean visitNode(TextEdit node) {
0968: if (node instanceof MultiTextEdit)
0969: return true;
0970:
0971: edits.add(node);
0972: return false;
0973: }
0974: });
0975: MultiTextEdit result = new MultiTextEdit();
0976: for (Iterator iterator = edits.iterator(); iterator.hasNext();) {
0977: TextEdit child = (TextEdit) iterator.next();
0978: child.getParent().removeChild(child);
0979: TextChangeCompatibility.insert(result, child);
0980: }
0981: return result;
0982: }
0983:
0984: private static boolean intersects(TextEdit edit1, TextEdit edit2) {
0985: if (edit1 instanceof MultiTextEdit
0986: && edit2 instanceof MultiTextEdit) {
0987: MultiTextEdit multiTextEdit1 = (MultiTextEdit) edit1;
0988: TextEdit[] children1 = multiTextEdit1.getChildren();
0989:
0990: MultiTextEdit multiTextEdit2 = (MultiTextEdit) edit2;
0991: TextEdit[] children2 = multiTextEdit2.getChildren();
0992:
0993: int i1 = 0;
0994: int i2 = 0;
0995: while (i1 < children1.length && i2 < children2.length) {
0996: while (children1[i1].getExclusiveEnd() < children2[i2]
0997: .getOffset()) {
0998: i1++;
0999: if (i1 >= children1.length)
1000: return false;
1001: }
1002: while (children2[i2].getExclusiveEnd() < children1[i1]
1003: .getOffset()) {
1004: i2++;
1005: if (i2 >= children2.length)
1006: return false;
1007: }
1008: if (intersects(children1[i1], children2[i2]))
1009: return true;
1010:
1011: if (children1[i1].getExclusiveEnd() < children2[i2]
1012: .getExclusiveEnd()) {
1013: i1++;
1014: } else {
1015: i2++;
1016: }
1017: }
1018:
1019: return false;
1020:
1021: } else if (edit1 instanceof MultiTextEdit) {
1022: MultiTextEdit multiTextEdit1 = (MultiTextEdit) edit1;
1023: TextEdit[] children = multiTextEdit1.getChildren();
1024: for (int i = 0; i < children.length; i++) {
1025: TextEdit child = children[i];
1026: if (intersects(child, edit2))
1027: return true;
1028: }
1029: return false;
1030:
1031: } else if (edit2 instanceof MultiTextEdit) {
1032: MultiTextEdit multiTextEdit2 = (MultiTextEdit) edit2;
1033: TextEdit[] children = multiTextEdit2.getChildren();
1034: for (int i = 0; i < children.length; i++) {
1035: TextEdit child = children[i];
1036: if (intersects(child, edit1))
1037: return true;
1038: }
1039: return false;
1040:
1041: } else {
1042: int start1 = edit1.getOffset();
1043: int end1 = start1 + edit1.getLength();
1044: int start2 = edit2.getOffset();
1045: int end2 = start2 + edit2.getLength();
1046:
1047: if (start1 > end2)
1048: return false;
1049:
1050: if (start2 > end1)
1051: return false;
1052:
1053: return true;
1054: }
1055: }
1056:
1057: private static TextEdit merge(TextEdit edit1, TextEdit edit2) {
1058: MultiTextEdit result = new MultiTextEdit();
1059: if (edit1 instanceof MultiTextEdit
1060: && edit2 instanceof MultiTextEdit) {
1061: MultiTextEdit multiTextEdit1 = (MultiTextEdit) edit1;
1062: TextEdit[] children1 = multiTextEdit1.getChildren();
1063:
1064: if (children1.length == 0)
1065: return edit2;
1066:
1067: MultiTextEdit multiTextEdit2 = (MultiTextEdit) edit2;
1068: TextEdit[] children2 = multiTextEdit2.getChildren();
1069:
1070: if (children2.length == 0)
1071: return edit1;
1072:
1073: int i1 = 0;
1074: int i2 = 0;
1075: while (i1 < children1.length && i2 < children2.length) {
1076:
1077: while (i1 < children1.length
1078: && children1[i1].getExclusiveEnd() < children2[i2]
1079: .getOffset()) {
1080: edit1.removeChild(0);
1081: result.addChild(children1[i1]);
1082: i1++;
1083: }
1084: if (i1 >= children1.length) {
1085: for (int i = i2; i < children2.length; i++) {
1086: edit2.removeChild(0);
1087: result.addChild(children2[i]);
1088: }
1089: return result;
1090: }
1091: while (i2 < children2.length
1092: && children2[i2].getExclusiveEnd() < children1[i1]
1093: .getOffset()) {
1094: edit2.removeChild(0);
1095: result.addChild(children2[i2]);
1096: i2++;
1097: }
1098: if (i2 >= children2.length) {
1099: for (int i = i1; i < children1.length; i++) {
1100: edit1.removeChild(0);
1101: result.addChild(children1[i]);
1102: }
1103: return result;
1104: }
1105:
1106: if (!(children1[i1].getExclusiveEnd() < children2[i2]
1107: .getOffset())) {
1108: edit1.removeChild(0);
1109: edit2.removeChild(0);
1110: result
1111: .addChild(merge(children1[i1],
1112: children2[i2]));
1113: i1++;
1114: i2++;
1115: }
1116: }
1117:
1118: return result;
1119: } else if (edit1 instanceof MultiTextEdit) {
1120: TextEdit[] children = edit1.getChildren();
1121:
1122: int i = 0;
1123: while (children[i].getExclusiveEnd() < edit2.getOffset()) {
1124: edit1.removeChild(0);
1125: result.addChild(children[i]);
1126: i++;
1127: if (i >= children.length) {
1128: result.addChild(edit2);
1129: return result;
1130: }
1131: }
1132: edit1.removeChild(0);
1133: result.addChild(merge(children[i], edit2));
1134: i++;
1135: while (i < children.length) {
1136: edit1.removeChild(0);
1137: result.addChild(children[i]);
1138: i++;
1139: }
1140:
1141: return result;
1142: } else if (edit2 instanceof MultiTextEdit) {
1143: TextEdit[] children = edit2.getChildren();
1144:
1145: int i = 0;
1146: while (children[i].getExclusiveEnd() < edit1.getOffset()) {
1147: edit2.removeChild(0);
1148: result.addChild(children[i]);
1149: i++;
1150: if (i >= children.length) {
1151: result.addChild(edit1);
1152: return result;
1153: }
1154: }
1155: edit2.removeChild(0);
1156: result.addChild(merge(edit1, children[i]));
1157: i++;
1158: while (i < children.length) {
1159: edit2.removeChild(0);
1160: result.addChild(children[i]);
1161: i++;
1162: }
1163:
1164: return result;
1165: } else {
1166: if (edit1.getExclusiveEnd() < edit2.getOffset()) {
1167: result.addChild(edit1);
1168: result.addChild(edit2);
1169: } else {
1170: result.addChild(edit2);
1171: result.addChild(edit1);
1172: }
1173:
1174: return result;
1175: }
1176: }
1177:
1178: /* (non-Javadoc)
1179: * @see org.eclipse.ltk.core.refactoring.Refactoring#getRefactoringTickProvider()
1180: */
1181: protected RefactoringTickProvider doGetRefactoringTickProvider() {
1182: return CLEAN_UP_REFACTORING_TICK_PROVIDER;
1183: }
1184:
1185: /**
1186: * {@inheritDoc}
1187: */
1188: public ISchedulingRule getSchedulingRule() {
1189: return ResourcesPlugin.getWorkspace().getRoot();
1190: }
1191:
1192: public static ICleanUp[] createCleanUps() {
1193: return new ICleanUp[] { new CodeStyleCleanUp(),
1194: new ControlStatementsCleanUp(),
1195: new ConvertLoopCleanUp(),
1196: new VariableDeclarationCleanUp(),
1197: new ExpressionsCleanUp(), new UnusedCodeCleanUp(),
1198: new Java50CleanUp(),
1199: new PotentialProgrammingProblemsCleanUp(),
1200: new UnnecessaryCodeCleanUp(), new StringCleanUp(),
1201: new UnimplementedCodeCleanUp(),
1202: new SortMembersCleanUp(), new ImportsCleanUp(),
1203: new CommentFormatCleanUp(), new CodeFormatCleanUp() };
1204: }
1205:
1206: public static ICleanUp[] createCleanUps(Map settings) {
1207: return new ICleanUp[] { new CodeStyleCleanUp(settings),
1208: new ControlStatementsCleanUp(settings),
1209: new ConvertLoopCleanUp(settings),
1210: new VariableDeclarationCleanUp(settings),
1211: new ExpressionsCleanUp(settings),
1212: new UnusedCodeCleanUp(settings),
1213: new Java50CleanUp(settings),
1214: new PotentialProgrammingProblemsCleanUp(settings),
1215: new UnnecessaryCodeCleanUp(settings),
1216: new StringCleanUp(settings),
1217: new UnimplementedCodeCleanUp(settings),
1218: new SortMembersCleanUp(settings),
1219: new ImportsCleanUp(settings),
1220: new CommentFormatCleanUp(settings),
1221: new CodeFormatCleanUp(settings) };
1222: }
1223: }
|