/******************************************************************************* * Copyright (c) 2000, 2009 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.wst.jsdt.internal.corext.fix; import java.util.ArrayList; import java.util.Arrays; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.ProjectScope; import org.eclipse.core.resources.ResourcesPlugin; 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.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.text.edits.MultiTextEdit; import org.eclipse.text.edits.TextEdit; import org.eclipse.text.edits.TextEditGroup; import org.eclipse.text.edits.TextEditVisitor; import org.eclipse.text.edits.UndoEdit; import org.eclipse.wst.jsdt.core.IJavaScriptProject; import org.eclipse.wst.jsdt.core.IJavaScriptUnit; import org.eclipse.wst.jsdt.core.IPackageFragment; import org.eclipse.wst.jsdt.core.JavaScriptModelException; import org.eclipse.wst.jsdt.core.WorkingCopyOwner; import org.eclipse.wst.jsdt.core.dom.ASTParser; import org.eclipse.wst.jsdt.core.dom.ASTRequestor; import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit; import org.eclipse.wst.jsdt.internal.corext.refactoring.Checks; import org.eclipse.wst.jsdt.internal.corext.refactoring.changes.CompilationUnitChange; import org.eclipse.wst.jsdt.internal.corext.refactoring.changes.DynamicValidationStateChange; import org.eclipse.wst.jsdt.internal.corext.refactoring.changes.MultiStateCompilationUnitChange; import org.eclipse.wst.jsdt.internal.corext.refactoring.changes.TextChangeCompatibility; import org.eclipse.wst.jsdt.internal.corext.refactoring.util.RefactoringASTParser; import org.eclipse.wst.jsdt.internal.corext.util.Messages; import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin; import org.eclipse.wst.jsdt.internal.ui.fix.CodeFormatCleanUp; import org.eclipse.wst.jsdt.internal.ui.fix.CommentFormatCleanUp; import org.eclipse.wst.jsdt.internal.ui.fix.ControlStatementsCleanUp; import org.eclipse.wst.jsdt.internal.ui.fix.ExpressionsCleanUp; import org.eclipse.wst.jsdt.internal.ui.fix.ICleanUp; import org.eclipse.wst.jsdt.internal.ui.fix.SortMembersCleanUp; import org.eclipse.wst.jsdt.internal.ui.fix.StringCleanUp; import org.eclipse.wst.jsdt.internal.ui.fix.UnusedCodeCleanUp; import org.eclipse.wst.jsdt.internal.ui.javaeditor.ASTProvider; import org.eclipse.wst.jsdt.internal.ui.refactoring.IScheduledRefactoring; import org.eclipse.wst.jsdt.ui.JavaScriptElementLabels; public class CleanUpRefactoring extends Refactoring implements IScheduledRefactoring { public static class CleanUpChange extends CompilationUnitChange { private UndoEdit fUndoEdit; public CleanUpChange(String name, IJavaScriptUnit cunit) { super(name, cunit); } /** * {@inheritDoc} */ protected Change createUndoChange(UndoEdit edit, ContentStamp stampToRestore) { fUndoEdit= edit; return super.createUndoChange(edit, stampToRestore); } public UndoEdit getUndoEdit() { return fUndoEdit; } } 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 IJavaScriptUnit fUnit; private final ICleanUp[] fCleanUpsArray; public ParseListElement(IJavaScriptUnit unit, ICleanUp[] cleanUps) { fUnit= unit; fCleanUpsArray= cleanUps; } public IJavaScriptUnit getCompilationUnit() { return fUnit; } 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} */ public void internalWorked(double work) { fRealWork+= work; } public void flush() { super.internalWorked(fRealWork); reset(); fFlushCount++; } public void reset() { fRealWork= 0.0; } public void done() {} public int getIndex() { return fIndex + fFlushCount; } public String getSubTaskMessage(IJavaScriptUnit source) { String typeName= source.getElementName(); 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/*<IJavaScriptUnit, Change>*/fSolutions; private final Hashtable/*<IJavaScriptUnit, ICleanUp[]>*/fCompilationUnitCleanUpMap; private final CleanUpRefactoringProgressMonitor fMonitor; public CleanUpASTRequestor(List parseList, Hashtable solutions, CleanUpRefactoringProgressMonitor monitor) { fSolutions= solutions; fMonitor= monitor; fUndoneElements= new ArrayList(); fCompilationUnitCleanUpMap= new Hashtable(parseList.size()); for (Iterator iter= parseList.iterator(); iter.hasNext();) { ParseListElement element= (ParseListElement)iter.next(); fCompilationUnitCleanUpMap.put(element.getCompilationUnit(), element.getCleanUps()); } } /** * {@inheritDoc} */ public void acceptAST(IJavaScriptUnit source, JavaScriptUnit ast) { fMonitor.subTask(fMonitor.getSubTaskMessage(source)); IJavaScriptUnit primary= (IJavaScriptUnit)source.getPrimaryElement(); ICleanUp[] cleanUps= (ICleanUp[])fCompilationUnitCleanUpMap.get(primary); ICleanUp[] rejectedCleanUps= calculateSolutions(source, ast, cleanUps); if (rejectedCleanUps.length > 0) { fUndoneElements.add(new ParseListElement(primary, rejectedCleanUps)); fMonitor.reset(); } else { fMonitor.flush(); } } public void acceptSource(IJavaScriptUnit source) { acceptAST(source, null); } public List getUndoneElements() { return fUndoneElements; } private ICleanUp[] calculateSolutions(IJavaScriptUnit source, JavaScriptUnit ast, ICleanUp[] cleanUps) { List/*<ICleanUp>*/result= new ArrayList(); CleanUpChange solution; try { solution= calculateChange(ast, source, cleanUps, result); } catch (CoreException e) { throw new FixCalculationException(e); } if (solution != null) { try { integrateSolution(solution, source); } catch (JavaScriptModelException e) { throw new FixCalculationException(e); } } return (ICleanUp[])result.toArray(new ICleanUp[result.size()]); } private void integrateSolution(CleanUpChange solution, IJavaScriptUnit source) throws JavaScriptModelException { IJavaScriptUnit primary= source.getPrimary(); List changes= (List)fSolutions.get(primary); if (changes == null) { changes= new ArrayList(); fSolutions.put(primary, changes); } changes.add(solution); } } private static abstract class CleanUpParser { private static final int MAX_AT_ONCE; static { long maxMemory= Runtime.getRuntime().maxMemory(); int ratio= (int)Math.round((double)maxMemory / (64 * 0x100000)); switch (ratio) { case 0: MAX_AT_ONCE= 25; break; case 1: MAX_AT_ONCE= 100; break; case 2: MAX_AT_ONCE= 200; break; case 3: MAX_AT_ONCE= 300; break; case 4: MAX_AT_ONCE= 400; break; default: MAX_AT_ONCE= 500; break; } } public void createASTs(IJavaScriptUnit[] units, String[] bindingKeys, CleanUpASTRequestor requestor, IProgressMonitor monitor) { if (monitor == null) monitor= new NullProgressMonitor(); try { monitor.beginTask("", units.length); //$NON-NLS-1$ List list= Arrays.asList(units); int end= 0; int cursor= 0; while (cursor < units.length) { end= Math.min(end + MAX_AT_ONCE, units.length); List toParse= list.subList(cursor, end); createParser().createASTs((IJavaScriptUnit[])toParse.toArray(new IJavaScriptUnit[toParse.size()]), bindingKeys, requestor, new SubProgressMonitor(monitor, toParse.size())); cursor= end; } } finally { monitor.done(); } } protected abstract ASTParser createParser(); } private class CleanUpFixpointIterator { private List/*<ParseListElement>*/fParseList; private final Hashtable/*<IJavaScriptUnit, List<CleanUpChange>>*/fSolutions; private final Hashtable/*<IJavaScriptUnit (primary), IJavaScriptUnit (working copy)>*/fWorkingCopies; private final IJavaScriptProject fProject; private final Map fCleanUpOptions; private final int fSize; private int fIndex; public CleanUpFixpointIterator(IJavaScriptProject project, IJavaScriptUnit[] units, ICleanUp[] cleanUps) { fProject= project; fSolutions= new Hashtable(units.length); fWorkingCopies= new Hashtable(); fParseList= new ArrayList(units.length); for (int i= 0; i < units.length; i++) { fParseList.add(new ParseListElement(units[i], cleanUps)); } fCleanUpOptions= new Hashtable(); for (int i= 0; i < cleanUps.length; i++) { ICleanUp cleanUp= cleanUps[i]; Map currentCleanUpOption= cleanUp.getRequiredOptions(); if (currentCleanUpOption != null) fCleanUpOptions.putAll(currentCleanUpOption); } fSize= units.length; fIndex= 1; } public boolean hasNext() { return !fParseList.isEmpty(); } public void next(IProgressMonitor monitor) throws CoreException { List parseList= new ArrayList(); List sourceList= new ArrayList(); try { for (Iterator iter= fParseList.iterator(); iter.hasNext();) { ParseListElement element= (ParseListElement)iter.next(); IJavaScriptUnit compilationUnit= element.getCompilationUnit(); if (fSolutions.containsKey(compilationUnit)) { if (fWorkingCopies.containsKey(compilationUnit)) { compilationUnit= (IJavaScriptUnit)fWorkingCopies.get(compilationUnit); } else { compilationUnit= compilationUnit.getWorkingCopy(new WorkingCopyOwner() {}, null); fWorkingCopies.put(compilationUnit.getPrimary(), compilationUnit); } applyChange(compilationUnit, (List)fSolutions.get(compilationUnit.getPrimary())); } if (requiresAST(compilationUnit, 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); CleanUpParser parser= new CleanUpParser() { protected ASTParser createParser() { ASTParser result= ASTParser.newParser(ASTProvider.SHARED_AST_LEVEL); result.setResolveBindings(true); result.setProject(fProject); Map options= RefactoringASTParser.getCompilerOptions(fProject); options.putAll(fCleanUpOptions); result.setCompilerOptions(options); return result; } }; try { IJavaScriptUnit[] units= (IJavaScriptUnit[])parseList.toArray(new IJavaScriptUnit[parseList.size()]); parser.createASTs(units, new String[0], requestor, cuMonitor); } catch (FixCalculationException e) { throw e.getException(); } for (Iterator iterator= sourceList.iterator(); iterator.hasNext();) { IJavaScriptUnit cu= (IJavaScriptUnit)iterator.next(); requestor.acceptSource(cu); } fParseList= requestor.getUndoneElements(); fIndex= cuMonitor.getIndex(); } finally { } } public void dispose() { for (Iterator iterator= fWorkingCopies.values().iterator(); iterator.hasNext();) { IJavaScriptUnit cu= (IJavaScriptUnit)iterator.next(); try { cu.discardWorkingCopy(); } catch (JavaScriptModelException e) { JavaScriptPlugin.log(e); } } } private boolean requiresAST(IJavaScriptUnit compilationUnit, ICleanUp[] cleanUps) throws CoreException { for (int i= 0; i < cleanUps.length; i++) { if (cleanUps[i].requireAST(compilationUnit)) return true; } return false; } public Change[] getResult() { Change[] result= new Change[fSolutions.size()]; int i=0; for (Iterator iterator= fSolutions.entrySet().iterator(); iterator.hasNext();) { Map.Entry entry= (Map.Entry)iterator.next(); List changes= (List)entry.getValue(); IJavaScriptUnit unit= (IJavaScriptUnit)entry.getKey(); int saveMode; try { if (fLeaveFilesDirty || unit.getBuffer().hasUnsavedChanges()) { saveMode= TextFileChange.LEAVE_DIRTY; } else { saveMode= TextFileChange.FORCE_SAVE; } } catch (JavaScriptModelException e) { saveMode= TextFileChange.LEAVE_DIRTY; JavaScriptPlugin.log(e); } if (changes.size() == 1) { CleanUpChange change= (CleanUpChange)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((CleanUpChange)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(IJavaScriptUnit compilationUnit, List changes) throws JavaScriptModelException, CoreException { if (changes.size() == 1) { CleanUpChange change= (CleanUpChange)changes.get(changes.size() - 1); compilationUnit.getBuffer().setContents(change.getPreviewContent(null)); } else { MultiStateCompilationUnitChange mscuc= new MultiStateCompilationUnitChange("", compilationUnit.getPrimary()); //$NON-NLS-1$ for (int i= 0; i < changes.size(); i++) { mscuc.addChange((CleanUpChange)changes.get(i)); } compilationUnit.getBuffer().setContents(mscuc.getPreviewContent(null)); } } } private static final RefactoringTickProvider CLEAN_UP_REFACTORING_TICK_PROVIDER= new RefactoringTickProvider(0, 1, 0, 0); private final List/*<ICleanUp>*/fCleanUps; private final Hashtable/*<IJavaScriptProject, List<IJavaScriptUnit>*/fProjects; private Change fChange; private boolean fLeaveFilesDirty; private final String fName; public CleanUpRefactoring() { this(FixMessages.CleanUpRefactoring_Refactoring_name); } public CleanUpRefactoring(String name) { fName= name; fCleanUps= new ArrayList(); fProjects= new Hashtable(); } public void addCompilationUnit(IJavaScriptUnit unit) { IJavaScriptProject javaProject= unit.getJavaScriptProject(); if (!fProjects.containsKey(javaProject)) fProjects.put(javaProject, new ArrayList()); List cus= (List)fProjects.get(javaProject); cus.add(unit); } public void clearCompilationUnits() { fProjects.clear(); } public boolean hasCompilationUnits() { return !fProjects.isEmpty(); } public IJavaScriptUnit[] getCompilationUnits() { List cus= new ArrayList(); for (Iterator iter= fProjects.values().iterator(); iter.hasNext();) { List pcus= (List)iter.next(); cus.addAll(pcus); } return (IJavaScriptUnit[])cus.toArray(new IJavaScriptUnit[cus.size()]); } public void addCleanUp(ICleanUp fix) { fCleanUps.add(fix); } public void clearCleanUps() { fCleanUps.clear(); } public boolean hasCleanUps() { return !fCleanUps.isEmpty(); } public ICleanUp[] getCleanUps() { return (ICleanUp[])fCleanUps.toArray(new ICleanUp[fCleanUps.size()]); } public IJavaScriptProject[] getProjects() { return (IJavaScriptProject[])fProjects.keySet().toArray(new IJavaScriptProject[fProjects.keySet().size()]); } public void setLeaveFilesDirty(boolean leaveFilesDirty) { fLeaveFilesDirty= leaveFilesDirty; } /* (non-Javadoc) * @see org.eclipse.ltk.core.refactoring.Refactoring#getName() */ public String getName() { return fName; } /* (non-Javadoc) * @see org.eclipse.ltk.core.refactoring.Refactoring#checkInitialConditions(org.eclipse.core.runtime.IProgressMonitor) */ 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) */ 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) */ 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= 0; for (Iterator iter= fProjects.keySet().iterator(); iter.hasNext();) { IJavaScriptProject project= (IJavaScriptProject)iter.next(); cuCount+= ((List)fProjects.get(project)).size(); } RefactoringStatus result= new RefactoringStatus(); pm.beginTask("", cuCount * 2 * fCleanUps.size() + 4 * fCleanUps.size()); //$NON-NLS-1$ try { DynamicValidationStateChange change= new DynamicValidationStateChange(getName()); change.setSchedulingRule(getSchedulingRule()); for (Iterator projectIter= fProjects.keySet().iterator(); projectIter.hasNext();) { IJavaScriptProject project= (IJavaScriptProject)projectIter.next(); List compilationUnits= (List)fProjects.get(project); IJavaScriptUnit[] cus= (IJavaScriptUnit[])compilationUnits.toArray(new IJavaScriptUnit[compilationUnits.size()]); ICleanUp[] cleanUps= (ICleanUp[])fCleanUps.toArray(new ICleanUp[fCleanUps.size()]); result.merge(initialize(project)); if (result.hasFatalError()) return result; result.merge(checkPreConditions(project, cus, new SubProgressMonitor(pm, 3 * cleanUps.length))); if (result.hasFatalError()) return result; Change[] changes= cleanUpProject(project, cus, 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 files= new ArrayList(); findFilesToBeModified(change, files); result.merge(Checks.validateModifiesFiles((IFile[])files.toArray(new IFile[files.size()]), getValidationContext())); if (result.hasFatalError()) return result; } finally { pm.done(); } return result; } private void findFilesToBeModified(CompositeChange change, List result) throws JavaScriptModelException { 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(IJavaScriptProject project, IJavaScriptUnit[] compilationUnits, ICleanUp[] cleanUps, IProgressMonitor monitor) throws CoreException { CleanUpFixpointIterator iter= new CleanUpFixpointIterator(project, compilationUnits, cleanUps); SubProgressMonitor subMonitor= new SubProgressMonitor(monitor, 2 * compilationUnits.length * cleanUps.length); subMonitor.beginTask("", compilationUnits.length); //$NON-NLS-1$ subMonitor.subTask(Messages.format(FixMessages.CleanUpRefactoring_Parser_Startup_message, project.getElementName())); try { while (iter.hasNext()) { iter.next(subMonitor); } return iter.getResult(); } finally { iter.dispose(); subMonitor.done(); } } private RefactoringStatus initialize(IJavaScriptProject javaProject) throws CoreException { Map options= CleanUpPreferenceUtil.loadOptions(new ProjectScope(javaProject.getProject())); if (options == null) { return RefactoringStatus.createFatalErrorStatus(Messages.format(FixMessages.CleanUpRefactoring_could_not_retrive_profile, javaProject.getElementName())); } ICleanUp[] cleanUps= getCleanUps(); for (int j= 0; j < cleanUps.length; j++) { cleanUps[j].initialize(options); } return new RefactoringStatus(); } private RefactoringStatus checkPreConditions(IJavaScriptProject javaProject, IJavaScriptUnit[] compilationUnits, IProgressMonitor monitor) throws CoreException { RefactoringStatus result= new RefactoringStatus(); ICleanUp[] cleanUps= getCleanUps(); monitor.beginTask("", compilationUnits.length * cleanUps.length); //$NON-NLS-1$ monitor.subTask(Messages.format(FixMessages.CleanUpRefactoring_Initialize_message, javaProject.getElementName())); 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))); if (result.hasFatalError()) return result; } } finally { monitor.done(); } return result; } private static String getChangeName(IJavaScriptUnit compilationUnit) { StringBuffer buf= new StringBuffer(); JavaScriptElementLabels.getCompilationUnitLabel(compilationUnit, JavaScriptElementLabels.ALL_DEFAULT, buf); buf.append(JavaScriptElementLabels.CONCAT_STRING); StringBuffer buf2= new StringBuffer(); JavaScriptElementLabels.getPackageFragmentLabel((IPackageFragment)compilationUnit.getParent(), JavaScriptElementLabels.P_QUALIFIED, buf2); buf.append(buf2.toString().replace('.', '/')); return buf.toString(); } public static CleanUpChange calculateChange(JavaScriptUnit ast, IJavaScriptUnit source, ICleanUp[] cleanUps, List undoneCleanUps) throws CoreException { if (cleanUps.length == 0) return null; CleanUpChange solution= null; int i= 0; do { ICleanUp cleanUp= cleanUps[i]; IFix fix; if (ast == null || !cleanUp.requireAST(source)) { fix= cleanUp.createFix(source); } else { fix= cleanUp.createFix(ast); } if (fix != null) { TextChange current= fix.createChange(); TextEdit currentEdit= pack(current.getEdit()); if (solution != null) { if (intersects(currentEdit, solution.getEdit())) { undoneCleanUps.add(cleanUp); } else { CleanUpChange merge= new CleanUpChange(FixMessages.CleanUpRefactoring_clean_up_multi_chang_name, source); merge.setEdit(merge(currentEdit, solution.getEdit())); copyChangeGroups(merge, solution); copyChangeGroups(merge, current); solution= merge; } } else { solution= new CleanUpChange(current.getName(), source); solution.setEdit(currentEdit); copyChangeGroups(solution, current); } } i++; } while (i < cleanUps.length && (solution == null || ast != null && !cleanUps[i].needsFreshAST(ast))); for (; i < cleanUps.length; i++) { undoneCleanUps.add(cleanUps[i]); } return solution; } private static void copyChangeGroups(CompilationUnitChange target, TextChange 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); } } private static TextEdit pack(TextEdit edit) { final List edits= new ArrayList(); edit.accept(new TextEditVisitor() { public boolean visitNode(TextEdit node) { if (node instanceof MultiTextEdit) return true; edits.add(node); return false; } }); MultiTextEdit result= new MultiTextEdit(); for (Iterator iterator= edits.iterator(); iterator.hasNext();) { TextEdit child= (TextEdit)iterator.next(); child.getParent().removeChild(child); TextChangeCompatibility.insert(result, child); } return result; } private static boolean intersects(TextEdit edit1, TextEdit edit2) { if (edit1 instanceof MultiTextEdit && edit2 instanceof MultiTextEdit) { MultiTextEdit multiTextEdit1= (MultiTextEdit)edit1; TextEdit[] children1= multiTextEdit1.getChildren(); MultiTextEdit multiTextEdit2= (MultiTextEdit)edit2; TextEdit[] children2= multiTextEdit2.getChildren(); int i1= 0; int i2= 0; while (i1 < children1.length && i2 < children2.length) { while (children1[i1].getExclusiveEnd() < children2[i2].getOffset()) { i1++; if (i1 >= children1.length) return false; } while (children2[i2].getExclusiveEnd() < children1[i1].getOffset()) { i2++; if (i2 >= children2.length) return false; } if (intersects(children1[i1], children2[i2])) return true; if (children1[i1].getExclusiveEnd() < children2[i2].getExclusiveEnd()) { i1++; } else { i2++; } } return false; } else if (edit1 instanceof MultiTextEdit) { MultiTextEdit multiTextEdit1= (MultiTextEdit)edit1; TextEdit[] children= multiTextEdit1.getChildren(); for (int i= 0; i < children.length; i++) { TextEdit child= children[i]; if (intersects(child, edit2)) return true; } return false; } else if (edit2 instanceof MultiTextEdit) { MultiTextEdit multiTextEdit2= (MultiTextEdit)edit2; TextEdit[] children= multiTextEdit2.getChildren(); for (int i= 0; i < children.length; i++) { TextEdit child= children[i]; if (intersects(child, edit1)) return true; } return false; } else { int start1= edit1.getOffset(); int end1= start1 + edit1.getLength(); int start2= edit2.getOffset(); int end2= start2 + edit2.getLength(); if (start1 > end2) return false; if (start2 > end1) return false; return true; } } private static TextEdit merge(TextEdit edit1, TextEdit edit2) { MultiTextEdit result= new MultiTextEdit(); if (edit1 instanceof MultiTextEdit && edit2 instanceof MultiTextEdit) { MultiTextEdit multiTextEdit1= (MultiTextEdit)edit1; TextEdit[] children1= multiTextEdit1.getChildren(); if (children1.length == 0) return edit2; MultiTextEdit multiTextEdit2= (MultiTextEdit)edit2; TextEdit[] children2= multiTextEdit2.getChildren(); if (children2.length == 0) return edit1; int i1= 0; int i2= 0; while (i1 < children1.length && i2 < children2.length) { while (i1 < children1.length && children1[i1].getExclusiveEnd() < children2[i2].getOffset()) { edit1.removeChild(0); result.addChild(children1[i1]); i1++; } if (i1 >= children1.length) { for (int i= i2; i < children2.length; i++) { edit2.removeChild(0); result.addChild(children2[i]); } return result; } while (i2 < children2.length && children2[i2].getExclusiveEnd() < children1[i1].getOffset()) { edit2.removeChild(0); result.addChild(children2[i2]); i2++; } if (i2 >= children2.length) { for (int i= i1; i < children1.length; i++) { edit1.removeChild(0); result.addChild(children1[i]); } return result; } if (!(children1[i1].getExclusiveEnd() < children2[i2].getOffset())) { edit1.removeChild(0); edit2.removeChild(0); result.addChild(merge(children1[i1], children2[i2])); i1++; i2++; } } return result; } else if (edit1 instanceof MultiTextEdit) { TextEdit[] children= edit1.getChildren(); int i= 0; while (children[i].getExclusiveEnd() < edit2.getOffset()) { edit1.removeChild(0); result.addChild(children[i]); i++; if (i >= children.length) { result.addChild(edit2); return result; } } edit1.removeChild(0); result.addChild(merge(children[i], edit2)); i++; while (i < children.length) { edit1.removeChild(0); result.addChild(children[i]); i++; } return result; } else if (edit2 instanceof MultiTextEdit) { TextEdit[] children= edit2.getChildren(); int i= 0; while (children[i].getExclusiveEnd() < edit1.getOffset()) { edit2.removeChild(0); result.addChild(children[i]); i++; if (i >= children.length) { result.addChild(edit1); return result; } } edit2.removeChild(0); result.addChild(merge(edit1, children[i])); i++; while (i < children.length) { edit2.removeChild(0); result.addChild(children[i]); i++; } return result; } else { if (edit1.getExclusiveEnd() < edit2.getOffset()) { result.addChild(edit1); result.addChild(edit2); } else { result.addChild(edit2); result.addChild(edit1); } return result; } } /* (non-Javadoc) * @see org.eclipse.ltk.core.refactoring.Refactoring#getRefactoringTickProvider() */ protected RefactoringTickProvider doGetRefactoringTickProvider() { return CLEAN_UP_REFACTORING_TICK_PROVIDER; } /** * {@inheritDoc} */ public ISchedulingRule getSchedulingRule() { return ResourcesPlugin.getWorkspace().getRoot(); } public static ICleanUp[] createCleanUps() { return new ICleanUp[] { // new CodeStyleCleanUp(), new ControlStatementsCleanUp(), // new ConvertLoopCleanUp(), // new VariableDeclarationCleanUp(), new ExpressionsCleanUp(), new UnusedCodeCleanUp(), // new Java50CleanUp(), // new UnnecessaryCodeCleanUp(), new StringCleanUp(), new SortMembersCleanUp(), // new ImportsCleanUp(), new CodeFormatCleanUp(), new CommentFormatCleanUp()}; } public static ICleanUp[] createCleanUps(Map settings) { return new ICleanUp[] { // new CodeStyleCleanUp(settings), new ControlStatementsCleanUp(settings), // new ConvertLoopCleanUp(settings), // new VariableDeclarationCleanUp(settings), new ExpressionsCleanUp(settings), new UnusedCodeCleanUp(settings), // new Java50CleanUp(settings), // new UnnecessaryCodeCleanUp(settings), new StringCleanUp(settings), new SortMembersCleanUp(settings), // new ImportsCleanUp(settings), new CodeFormatCleanUp(settings), new CommentFormatCleanUp(settings)}; } }