Copyright (c) 2000, 2019 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 Red Hat Inc, - copied to jdt.core.manipulation Microsoft Corporation - Add fFormattingOptions field - https://bugs.eclipse.org/551601
/******************************************************************************* * Copyright (c) 2000, 2019 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 * Red Hat Inc, - copied to jdt.core.manipulation * Microsoft Corporation - Add fFormattingOptions field - https://bugs.eclipse.org/551601 *******************************************************************************/
package org.eclipse.jdt.internal.corext.refactoring.structure; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.text.edits.MultiTextEdit; import org.eclipse.text.edits.TextEdit; import org.eclipse.text.edits.TextEditGroup; 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.GroupCategorySet; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.WorkingCopyOwner; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; import org.eclipse.jdt.core.manipulation.CodeStyleConfiguration; import org.eclipse.jdt.core.refactoring.CompilationUnitChange; import org.eclipse.jdt.internal.core.manipulation.JavaManipulationPlugin; import org.eclipse.jdt.internal.corext.dom.IASTSharedValues; import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
A CompilationUnitRewrite holds all data structures that are typically required for non-trivial refactorings. All getters are initialized lazily to avoid lengthy processing in Refactoring.checkInitialConditions(IProgressMonitor).

Bindings are resolved by default, but can be disabled with setResolveBindings(false). Statements recovery is enabled by default, but can be disabled with setStatementsRecovery(false). Bindings recovery is disabled by default, but can be enabled with setBindingRecovery(true).

see JDTUIHelperClasses
/** * 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> * * see JDTUIHelperClasses */
public class CompilationUnitRewrite { //TODO: add RefactoringStatus fStatus;? private ICompilationUnit fCu; private List<TextEditGroup> fTextEditGroups= new ArrayList<>(); 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 final WorkingCopyOwner fOwner; private IDocument fRememberContent= null; private Map<String, String> fFormattingOptions; public CompilationUnitRewrite(ICompilationUnit cu) { this(null, cu, null, null); } public CompilationUnitRewrite(WorkingCopyOwner owner, ICompilationUnit cu) { this(owner, cu, null, null); } public CompilationUnitRewrite(ICompilationUnit cu, CompilationUnit root) { this(null, cu, root, null); } public CompilationUnitRewrite(WorkingCopyOwner owner, ICompilationUnit cu, CompilationUnit root) { this(owner, cu, root, null); } public CompilationUnitRewrite(WorkingCopyOwner owner, ICompilationUnit cu, CompilationUnit root, Map<String, String> options) { fOwner= owner; fCu= cu; fRoot= root; fFormattingOptions = options; } public void rememberContent() { fRememberContent= new Document(); }
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 getRoot(),getASTRewrite(), getImportRemover(). This method has no effect if the target object has been created with CompilationUnitRewrite(ICompilationUnit, CompilationUnit).

Defaults to true (do resolve bindings).

Params:
  • resolve – true if bindings are wanted, and false if bindings are not of interest
See Also:
/** * 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 getRoot(),getASTRewrite(), getImportRemover(). This method has no effect if the target object has been created with CompilationUnitRewrite(ICompilationUnit, CompilationUnit).

Defaults to true (do perform statements recovery).

Params:
  • statementsRecovery – whether statements recovery should be performed
See Also:
/** * 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 getRoot(),getASTRewrite(), getImportRemover(). This method has no effect if the target object has been created with CompilationUnitRewrite(ICompilationUnit, CompilationUnit).

Defaults to false (do not perform bindings recovery).

Params:
  • bindingsRecovery – whether bindings recovery should be performed
See Also:
/** * 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<>(); } public void clearImportRewrites() { fImportRewrite= null; fImportRemover= null; } public void clearASTAndImportRewrites() { clearASTRewrite(); clearImportRewrites(); } 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.
Params:
  • generateGroups – true to generate text edit groups, false otherwise
Throws:
Returns:a CompilationUnitChange, or null for an empty change
Since:3.6
/** * Creates a compilation unit change based on the events recorded by this compilation unit * rewrite. * * @param generateGroups <code>true</code> to generate text edit groups, <code>false</code> otherwise * @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 * @since 3.6 */
public CompilationUnitChange createChange(boolean generateGroups) throws CoreException { return createChange(generateGroups, null); }
Creates a compilation unit change based on the events recorded by this compilation unit rewrite.

DO NOT REMOVE, used in a product.

Throws:
Returns:a CompilationUnitChange, or null for an empty change
Deprecated:since 3.5, replaced by createChange(boolean)
/** * Creates a compilation unit change based on the events recorded by this compilation unit * rewrite. * <p> * DO NOT REMOVE, used in a product.</p> * * @return a {@link org.eclipse.jdt.core.refactoring.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 * @deprecated since 3.5, replaced by {@link #createChange(boolean)} */
@Deprecated public org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange createChange() throws CoreException { CompilationUnitChange change= createChange(true); if (change == null) return null; return new org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange(change); }
Creates a compilation unit change based on the events recorded by this compilation unit rewrite.
Params:
  • generateGroups – true to generate text edit groups, false otherwise
  • monitor – the progress monitor or null
Throws:
Returns:a CompilationUnitChange, or null for an empty change
/** * Creates a compilation unit change based on the events recorded by this compilation unit * rewrite. * * @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(boolean generateGroups, IProgressMonitor monitor) throws CoreException { return createChange(fCu.getElementName(), generateGroups, monitor); }
Creates a compilation unit change based on the events recorded by this compilation unit rewrite.
Params:
  • name – the name of the change to create
  • generateGroups – true to generate text edit groups, false otherwise
  • monitor – the progress monitor or null
Throws:
Returns:a CompilationUnitChange, or null for an empty change
/** * 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, IProgressMonitor monitor) throws CoreException { CompilationUnitChange cuChange= new CompilationUnitChange(name, fCu); MultiTextEdit multiEdit= new MultiTextEdit(); cuChange.setEdit(multiEdit); return attachChange(cuChange, generateGroups, monitor); }
Attaches the changes of this compilation unit rewrite to the given CU Change. The given change must either have no root edit, or a MultiTextEdit as a root edit. The edits in the given change must not overlap with the changes of this compilation unit.
Params:
  • cuChange – existing CompilationUnitChange with a MultiTextEdit root or no root at all.
  • generateGroups – true to generate text edit groups, false otherwise
  • monitor – the progress monitor or null
Throws:
  • CoreException – when text buffer acquisition or import rewrite text edit creation fails
Returns:a change combining the changes of this rewrite and the given rewrite, or null for an empty change
/** * 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, IProgressMonitor monitor) throws CoreException { try { 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) { // clean up garbage from earlier calls to ASTRewrite#rewriteAST(..), see https://bugs.eclipse.org/bugs/show_bug.cgi?id=408334#c2 clearGroupDescriptionEdits(); TextEdit rewriteEdit; if (fRememberContent != null) { rewriteEdit= fRewrite.rewriteAST(fRememberContent, fFormattingOptions == null ? fCu.getJavaProject().getOptions(true) : fFormattingOptions); } else { if (fFormattingOptions == null) { rewriteEdit= fRewrite.rewriteAST(); } else { rewriteEdit= fRewrite.rewriteAST(new Document(fCu.getSource()), fFormattingOptions); } } 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(monitor); if (!isEmptyEdit(importsEdit)) { multiEdit.addChild(importsEdit); String importUpdateName= RefactoringCoreMessages.ASTData_update_imports; cuChange.addTextEditGroup(new TextEditGroup(importUpdateName, importsEdit)); } } else { } if (isEmptyEdit(multiEdit)) return null; return cuChange; } finally { if (monitor != null) monitor.done(); } } 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(IASTSharedValues.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.set(fCu.getSource()); } 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= CodeStyleConfiguration.createImportRewrite(fCu, true); } else { fImportRewrite= createImportRewrite(getRoot(), true); } } catch (CoreException e) { JavaManipulationPlugin.log(e); throw new IllegalStateException(e.getMessage()); // like ASTParser#createAST(..) does } } return fImportRewrite; } private ImportRewrite createImportRewrite(CompilationUnit astRoot, boolean restoreExistingImports) { ImportRewrite rewrite= CodeStyleConfiguration.createImportRewrite(astRoot, restoreExistingImports); if (astRoot.getAST().hasResolvedBindings()) { rewrite.setUseContextToFilterImplicitImports(true); } return rewrite; } public ImportRemover getImportRemover() { if (fImportRemover == null) { fImportRemover= new ImportRemover(fCu.getJavaProject(), getRoot()); } return fImportRemover; } private void clearGroupDescriptionEdits() { for (Iterator<TextEditGroup> iter= fTextEditGroups.iterator(); iter.hasNext();) { TextEditGroup group= iter.next(); group.clearTextEdits(); } } public Map<String, String> getFormattingOptions() { return fFormattingOptions; } public void setFormattingOptions(Map<String, String> formattingOptions) { fFormattingOptions = formattingOptions; } }