/******************************************************************************* * Copyright (c) 2000, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.che.ide.ext.java.jdt.internal.corext.refactoring.structure; import org.eclipse.che.ide.ext.java.jdt.core.dom.AST; import org.eclipse.che.ide.ext.java.jdt.core.dom.CompilationUnit; import org.eclipse.che.ide.ext.java.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.che.ide.ext.java.jdt.core.dom.rewrite.ImportRewrite; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.che.ide.ext.java.jdt.internal.corext.codemanipulation.StubUtility; import org.eclipse.che.ide.ext.java.jdt.internal.corext.refactoring.RefactoringCoreMessages; import org.eclipse.che.ide.ext.java.jdt.internal.corext.refactoring.code.CompilationUnitChange; import org.eclipse.che.ide.ext.java.jdt.refactoring.CategorizedTextEditGroup; import org.eclipse.che.ide.ext.java.jdt.refactoring.GroupCategorySet; import org.eclipse.che.ide.ext.java.worker.WorkerDocument; import org.eclipse.che.ide.ext.java.worker.WorkerMessageHandler; import org.eclipse.che.ide.ext.java.jdt.text.Document; import org.eclipse.che.ide.ext.java.jdt.text.edits.MultiTextEdit; import org.eclipse.che.ide.ext.java.jdt.text.edits.TextEdit; import org.eclipse.che.ide.ext.java.jdt.text.edits.TextEditGroup; import org.eclipse.che.ide.runtime.CoreException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * A {@link CompilationUnitRewrite} holds all data structures that are typically * required for non-trivial refactorings. All getters are initialized lazily to * avoid lengthy processing in * {@link org.eclipse.ltk.core.refactoring.Refactoring#checkInitialConditions(org.eclipse.core.runtime.IProgressMonitor)}. * <p> * Bindings are resolved by default, but can be disabled with <code>setResolveBindings(false)</code>. * Statements recovery is enabled by default, but can be disabled with <code>setStatementsRecovery(false)</code>. * Bindings recovery is disabled by default, but can be enabled with <code>setBindingRecovery(true)</code>. * </p> */ public class CompilationUnitRewrite { //TODO: add RefactoringStatus fStatus;? // private ICompilationUnit fCu; private List<TextEditGroup> fTextEditGroups = new ArrayList<TextEditGroup>(); private CompilationUnit fRoot; // lazily initialized private ASTRewrite fRewrite; // lazily initialized private ImportRewrite fImportRewrite; // lazily initialized private ImportRemover fImportRemover; // lazily initialized private boolean fResolveBindings = true; private boolean fStatementsRecovery = true; private boolean fBindingsRecovery = false; private Document fRememberContent = null; private final Document document; // public CompilationUnitRewrite(ICompilationUnit cu) { // this(null, cu, null); // } // // public CompilationUnitRewrite(WorkingCopyOwner owner, ICompilationUnit cu) { // this(owner, cu, null); // } // // public CompilationUnitRewrite(ICompilationUnit cu, CompilationUnit root) { // this(null, cu, root); // } public CompilationUnitRewrite(CompilationUnit root, Document document) { // fOwner= owner; // fCu= cu; fRoot = root; this.document = document; } public void rememberContent() { fRememberContent = new WorkerDocument(); } /** * Controls whether the compiler should provide binding information for the AST * nodes it creates. To be effective, this method must be called before any * of {@link #getRoot()},{@link #getASTRewrite()}, * {@link #getImportRemover()}. This method has no effect if the target object * has been created with {@link #CompilationUnitRewrite(ICompilationUnit, CompilationUnit)}. * <p> * Defaults to <b><code>true</code></b> (do resolve bindings). * </p> * * @param resolve * <code>true</code> if bindings are wanted, and * <code>false</code> if bindings are not of interest * @see org.eclipse.jdt.core.dom.ASTParser#setResolveBindings(boolean) * Note: The default value (<code>true</code>) differs from the one of * the corresponding method in ASTParser. */ public void setResolveBindings(boolean resolve) { fResolveBindings = resolve; } /** * Controls whether the compiler should perform statements recovery. * To be effective, this method must be called before any * of {@link #getRoot()},{@link #getASTRewrite()}, * {@link #getImportRemover()}. This method has no effect if the target object * has been created with {@link #CompilationUnitRewrite(ICompilationUnit, CompilationUnit)}. * <p> * Defaults to <b><code>true</code></b> (do perform statements recovery). * </p> * * @param statementsRecovery * whether statements recovery should be performed * @see org.eclipse.jdt.core.dom.ASTParser#setStatementsRecovery(boolean) */ public void setStatementsRecovery(boolean statementsRecovery) { fStatementsRecovery = statementsRecovery; } /** * Controls whether the compiler should perform bindings recovery. * To be effective, this method must be called before any * of {@link #getRoot()},{@link #getASTRewrite()}, * {@link #getImportRemover()}. This method has no effect if the target object * has been created with {@link #CompilationUnitRewrite(ICompilationUnit, CompilationUnit)}. * <p> * Defaults to <b><code>false</code></b> (do not perform bindings recovery). * </p> * * @param bindingsRecovery * whether bindings recovery should be performed * @see org.eclipse.jdt.core.dom.ASTParser#setBindingsRecovery(boolean) */ public void setBindingRecovery(boolean bindingsRecovery) { fBindingsRecovery = bindingsRecovery; } public void clearASTRewrite() { fRewrite = null; fTextEditGroups = new ArrayList<TextEditGroup>(); } public void clearImportRewrites() { fImportRewrite = null; } public void clearASTAndImportRewrites() { clearASTRewrite(); fImportRewrite = null; } public CategorizedTextEditGroup createCategorizedGroupDescription(String name, GroupCategorySet set) { CategorizedTextEditGroup result = new CategorizedTextEditGroup(name, set); fTextEditGroups.add(result); return result; } public TextEditGroup createGroupDescription(String name) { TextEditGroup result = new TextEditGroup(name); fTextEditGroups.add(result); return result; } /** * Creates a compilation unit change based on the events recorded by this compilation unit rewrite. * * @param name * the name of the change to create * @param generateGroups * <code>true</code> to generate text edit groups, <code>false</code> otherwise * @param monitor * the progress monitor or <code>null</code> * @return a {@link CompilationUnitChange}, or <code>null</code> for an empty change * @throws CoreException * when text buffer acquisition or import rewrite text edit creation fails * @throws IllegalArgumentException * when the AST rewrite encounters problems */ public CompilationUnitChange createChange(String name, boolean generateGroups) throws CoreException { CompilationUnitChange cuChange = new CompilationUnitChange(name, document); MultiTextEdit multiEdit = new MultiTextEdit(); cuChange.setEdit(multiEdit); return attachChange(cuChange, generateGroups); } /** * Attaches the changes of this compilation unit rewrite to the given CU Change. The given * change <b>must</b> either have no root edit, or a MultiTextEdit as a root edit. * The edits in the given change <b>must not</b> overlap with the changes of * this compilation unit. * * @param cuChange * existing CompilationUnitChange with a MultiTextEdit root or no root at all. * @param generateGroups * <code>true</code> to generate text edit groups, <code>false</code> otherwise * @param monitor * the progress monitor or <code>null</code> * @return a change combining the changes of this rewrite and the given rewrite, or <code>null</code> for an empty change * @throws CoreException * when text buffer acquisition or import rewrite text edit creation fails */ public CompilationUnitChange attachChange(CompilationUnitChange cuChange, boolean generateGroups) throws CoreException { boolean needsAstRewrite = fRewrite != null; // TODO: do we need something like ASTRewrite#hasChanges() here? boolean needsImportRemoval = fImportRemover != null && fImportRemover.hasRemovedNodes(); boolean needsImportRewrite = fImportRewrite != null && fImportRewrite.hasRecordedChanges() || needsImportRemoval; if (!needsAstRewrite && !needsImportRemoval && !needsImportRewrite) return null; MultiTextEdit multiEdit = (MultiTextEdit)cuChange.getEdit(); if (multiEdit == null) { multiEdit = new MultiTextEdit(); cuChange.setEdit(multiEdit); } if (needsAstRewrite) { TextEdit rewriteEdit; if (fRememberContent != null) { rewriteEdit = fRewrite.rewriteAST(fRememberContent, WorkerMessageHandler.get().getOptions()); } else { rewriteEdit = fRewrite.rewriteAST(document, WorkerMessageHandler.get().getOptions()); } if (!isEmptyEdit(rewriteEdit)) { multiEdit.addChild(rewriteEdit); if (generateGroups) { for (Iterator<TextEditGroup> iter = fTextEditGroups.iterator(); iter.hasNext(); ) { TextEditGroup group = iter.next(); cuChange.addTextEditGroup(group); } } } } if (needsImportRemoval) { fImportRemover.applyRemoves(getImportRewrite()); } if (needsImportRewrite) { TextEdit importsEdit = fImportRewrite.rewriteImports(); if (!isEmptyEdit(importsEdit)) { multiEdit.addChild(importsEdit); String importUpdateName = RefactoringCoreMessages.INSTANCE.ASTData_update_imports(); cuChange.addTextEditGroup(new TextEditGroup(importUpdateName, importsEdit)); } } else { } if (isEmptyEdit(multiEdit)) return null; return cuChange; } private static boolean isEmptyEdit(TextEdit edit) { return edit.getClass() == MultiTextEdit.class && !edit.hasChildren(); } // public ICompilationUnit getCu() { // return fCu; // } public CompilationUnit getRoot() { // if (fRoot == null) // fRoot= new RefactoringASTParser(ASTProvider.SHARED_AST_LEVEL).parse(fCu, fOwner, fResolveBindings, fStatementsRecovery, // fBindingsRecovery, null); return fRoot; } public AST getAST() { return getRoot().getAST(); } public ASTRewrite getASTRewrite() { if (fRewrite == null) { fRewrite = ASTRewrite.create(getRoot().getAST()); if (fRememberContent != null) { // wain until ast rewrite is accessed first // try // { fRememberContent = document; // } // catch (JavaModelException e) // { // fRememberContent = null; // } } } return fRewrite; } public ImportRewrite getImportRewrite() { if (fImportRewrite == null) { // lazily initialized to avoid lengthy processing in checkInitialConditions(..) // try // { /* If bindings are to be resolved, then create the AST, so that * ImportRewrite#setUseContextToFilterImplicitImports(boolean) will be set to true * and ContextSensitiveImportRewriteContext etc. can be used. */ if (fRoot == null && !fResolveBindings) { fImportRewrite = StubUtility.createImportRewrite(document, getRoot(), true); } else { fImportRewrite = StubUtility.createImportRewrite(document, getRoot(), true); } // } // catch (CoreException e) // { //// JavaPlugin.log(e); // throw new IllegalStateException(e.getMessage()); // like ASTParser#createAST(..) does // } } return fImportRewrite; } public ImportRemover getImportRemover() { if (fImportRemover == null) { fImportRemover = new ImportRemover(getRoot()); } return fImportRemover; } public void clearGroupDescriptions() { for (Iterator<TextEditGroup> iter = fTextEditGroups.iterator(); iter.hasNext(); ) { TextEditGroup group = iter.next(); group.clearTextEdits(); } } }