]>
Commit | Line | Data |
---|---|---|
1b2798f6 EK |
1 | /******************************************************************************* |
2 | * Copyright (c) 2000, 2011 IBM Corporation and others. | |
3 | * All rights reserved. This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License v1.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * http://www.eclipse.org/legal/epl-v10.html | |
7 | * | |
8 | * Contributors: | |
9 | * IBM Corporation - initial API and implementation | |
10 | *******************************************************************************/ | |
11 | package org.eclipse.jdt.internal.corext.refactoring.structure; | |
12 | ||
13 | import java.util.ArrayList; | |
14 | import java.util.Iterator; | |
15 | import java.util.List; | |
16 | ||
17 | import org.eclipse.core.runtime.CoreException; | |
18 | import org.eclipse.core.runtime.IProgressMonitor; | |
19 | ||
20 | import org.eclipse.text.edits.MultiTextEdit; | |
21 | import org.eclipse.text.edits.TextEdit; | |
22 | import org.eclipse.text.edits.TextEditGroup; | |
23 | ||
24 | import org.eclipse.jface.text.Document; | |
25 | import org.eclipse.jface.text.IDocument; | |
26 | ||
27 | import org.eclipse.ltk.core.refactoring.CategorizedTextEditGroup; | |
28 | import org.eclipse.ltk.core.refactoring.GroupCategorySet; | |
29 | ||
30 | import org.eclipse.jdt.core.ICompilationUnit; | |
31 | import org.eclipse.jdt.core.JavaModelException; | |
32 | import org.eclipse.jdt.core.WorkingCopyOwner; | |
33 | import org.eclipse.jdt.core.dom.AST; | |
34 | import org.eclipse.jdt.core.dom.CompilationUnit; | |
35 | import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; | |
36 | import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; | |
37 | import org.eclipse.jdt.core.refactoring.CompilationUnitChange; | |
38 | ||
39 | import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility; | |
40 | import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; | |
41 | import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser; | |
42 | ||
43 | import org.eclipse.jdt.internal.ui.JavaPlugin; | |
44 | import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider; | |
45 | ||
46 | /** | |
47 | * A {@link CompilationUnitRewrite} holds all data structures that are typically | |
48 | * required for non-trivial refactorings. All getters are initialized lazily to | |
49 | * avoid lengthy processing in | |
50 | * {@link org.eclipse.ltk.core.refactoring.Refactoring#checkInitialConditions(org.eclipse.core.runtime.IProgressMonitor)}. | |
51 | * <p> | |
52 | * Bindings are resolved by default, but can be disabled with <code>setResolveBindings(false)</code>. | |
53 | * Statements recovery is enabled by default, but can be disabled with <code>setStatementsRecovery(false)</code>. | |
54 | * Bindings recovery is disabled by default, but can be enabled with <code>setBindingRecovery(true)</code>. | |
55 | * </p> | |
56 | */ | |
57 | public class CompilationUnitRewrite { | |
58 | //TODO: add RefactoringStatus fStatus;? | |
59 | private ICompilationUnit fCu; | |
60 | private List<TextEditGroup> fTextEditGroups= new ArrayList<TextEditGroup>(); | |
61 | ||
62 | private CompilationUnit fRoot; // lazily initialized | |
63 | private ASTRewrite fRewrite; // lazily initialized | |
64 | private ImportRewrite fImportRewrite; // lazily initialized | |
65 | private ImportRemover fImportRemover; // lazily initialized | |
66 | private boolean fResolveBindings= true; | |
67 | private boolean fStatementsRecovery= true; | |
68 | private boolean fBindingsRecovery= false; | |
69 | private final WorkingCopyOwner fOwner; | |
70 | private IDocument fRememberContent= null; | |
71 | ||
72 | ||
73 | public CompilationUnitRewrite(ICompilationUnit cu) { | |
74 | this(null, cu, null); | |
75 | } | |
76 | ||
77 | public CompilationUnitRewrite(WorkingCopyOwner owner, ICompilationUnit cu) { | |
78 | this(owner, cu, null); | |
79 | } | |
80 | ||
81 | public CompilationUnitRewrite(ICompilationUnit cu, CompilationUnit root) { | |
82 | this(null, cu, root); | |
83 | } | |
84 | ||
85 | public CompilationUnitRewrite(WorkingCopyOwner owner, ICompilationUnit cu, CompilationUnit root) { | |
86 | fOwner= owner; | |
87 | fCu= cu; | |
88 | fRoot= root; | |
89 | } | |
90 | ||
91 | public void rememberContent() { | |
92 | fRememberContent= new Document(); | |
93 | } | |
94 | ||
95 | ||
96 | /** | |
97 | * Controls whether the compiler should provide binding information for the AST | |
98 | * nodes it creates. To be effective, this method must be called before any | |
99 | * of {@link #getRoot()},{@link #getASTRewrite()}, | |
100 | * {@link #getImportRemover()}. This method has no effect if the target object | |
101 | * has been created with {@link #CompilationUnitRewrite(ICompilationUnit, CompilationUnit)}. | |
102 | * <p> | |
103 | * Defaults to <b><code>true</code></b> (do resolve bindings). | |
104 | * </p> | |
105 | * | |
106 | * @param resolve | |
107 | * <code>true</code> if bindings are wanted, and | |
108 | * <code>false</code> if bindings are not of interest | |
109 | * @see org.eclipse.jdt.core.dom.ASTParser#setResolveBindings(boolean) | |
110 | * Note: The default value (<code>true</code>) differs from the one of | |
111 | * the corresponding method in ASTParser. | |
112 | */ | |
113 | public void setResolveBindings(boolean resolve) { | |
114 | fResolveBindings= resolve; | |
115 | } | |
116 | ||
117 | /** | |
118 | * Controls whether the compiler should perform statements recovery. | |
119 | * To be effective, this method must be called before any | |
120 | * of {@link #getRoot()},{@link #getASTRewrite()}, | |
121 | * {@link #getImportRemover()}. This method has no effect if the target object | |
122 | * has been created with {@link #CompilationUnitRewrite(ICompilationUnit, CompilationUnit)}. | |
123 | * <p> | |
124 | * Defaults to <b><code>true</code></b> (do perform statements recovery). | |
125 | * </p> | |
126 | * | |
127 | * @param statementsRecovery whether statements recovery should be performed | |
128 | * @see org.eclipse.jdt.core.dom.ASTParser#setStatementsRecovery(boolean) | |
129 | */ | |
130 | public void setStatementsRecovery(boolean statementsRecovery) { | |
131 | fStatementsRecovery= statementsRecovery; | |
132 | } | |
133 | ||
134 | /** | |
135 | * Controls whether the compiler should perform bindings recovery. | |
136 | * To be effective, this method must be called before any | |
137 | * of {@link #getRoot()},{@link #getASTRewrite()}, | |
138 | * {@link #getImportRemover()}. This method has no effect if the target object | |
139 | * has been created with {@link #CompilationUnitRewrite(ICompilationUnit, CompilationUnit)}. | |
140 | * <p> | |
141 | * Defaults to <b><code>false</code></b> (do not perform bindings recovery). | |
142 | * </p> | |
143 | * | |
144 | * @param bindingsRecovery whether bindings recovery should be performed | |
145 | * @see org.eclipse.jdt.core.dom.ASTParser#setBindingsRecovery(boolean) | |
146 | */ | |
147 | public void setBindingRecovery(boolean bindingsRecovery) { | |
148 | fBindingsRecovery= bindingsRecovery; | |
149 | } | |
150 | ||
151 | public void clearASTRewrite() { | |
152 | fRewrite= null; | |
153 | fTextEditGroups= new ArrayList<TextEditGroup>(); | |
154 | } | |
155 | ||
156 | public void clearImportRewrites() { | |
157 | fImportRewrite= null; | |
158 | } | |
159 | ||
160 | public void clearASTAndImportRewrites() { | |
161 | clearASTRewrite(); | |
162 | fImportRewrite= null; | |
163 | } | |
164 | ||
165 | public CategorizedTextEditGroup createCategorizedGroupDescription(String name, GroupCategorySet set) { | |
166 | CategorizedTextEditGroup result= new CategorizedTextEditGroup(name, set); | |
167 | fTextEditGroups.add(result); | |
168 | return result; | |
169 | } | |
170 | ||
171 | public TextEditGroup createGroupDescription(String name) { | |
172 | TextEditGroup result= new TextEditGroup(name); | |
173 | fTextEditGroups.add(result); | |
174 | return result; | |
175 | } | |
176 | ||
177 | /** | |
178 | * Creates a compilation unit change based on the events recorded by this compilation unit | |
179 | * rewrite. | |
180 | * | |
181 | * @param generateGroups <code>true</code> to generate text edit groups, <code>false</code> otherwise | |
182 | * @return a {@link CompilationUnitChange}, or <code>null</code> for an empty change | |
183 | * @throws CoreException when text buffer acquisition or import rewrite text edit creation fails | |
184 | * @throws IllegalArgumentException when the AST rewrite encounters problems | |
185 | * @since 3.6 | |
186 | */ | |
187 | public CompilationUnitChange createChange(boolean generateGroups) throws CoreException { | |
188 | return createChange(generateGroups, null); | |
189 | } | |
190 | ||
191 | /** | |
192 | * Creates a compilation unit change based on the events recorded by this compilation unit | |
193 | * rewrite. | |
194 | * <p> | |
195 | * DO NOT REMOVE, used in a product.</p> | |
196 | * | |
197 | * @return a {@link org.eclipse.jdt.core.refactoring.CompilationUnitChange}, or <code>null</code> for an empty change | |
198 | * @throws CoreException when text buffer acquisition or import rewrite text edit creation fails | |
199 | * @throws IllegalArgumentException when the AST rewrite encounters problems | |
200 | * @deprecated since 3.5, replaced by {@link #createChange(boolean)} | |
201 | */ | |
202 | public org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange createChange() throws CoreException { | |
203 | CompilationUnitChange change= createChange(true); | |
204 | if (change == null) | |
205 | return null; | |
206 | return new org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange(change); | |
207 | } | |
208 | ||
209 | /** | |
210 | * Creates a compilation unit change based on the events recorded by this compilation unit | |
211 | * rewrite. | |
212 | * | |
213 | * @param generateGroups <code>true</code> to generate text edit groups, <code>false</code> | |
214 | * otherwise | |
215 | * @param monitor the progress monitor or <code>null</code> | |
216 | * @return a {@link CompilationUnitChange}, or <code>null</code> for an empty change | |
217 | * @throws CoreException when text buffer acquisition or import rewrite text edit creation fails | |
218 | * @throws IllegalArgumentException when the AST rewrite encounters problems | |
219 | */ | |
220 | public CompilationUnitChange createChange(boolean generateGroups, IProgressMonitor monitor) throws CoreException { | |
221 | return createChange(fCu.getElementName(), generateGroups, monitor); | |
222 | } | |
223 | ||
224 | /** | |
225 | * Creates a compilation unit change based on the events recorded by this compilation unit rewrite. | |
226 | * @param name the name of the change to create | |
227 | * @param generateGroups <code>true</code> to generate text edit groups, <code>false</code> otherwise | |
228 | * @param monitor the progress monitor or <code>null</code> | |
229 | * @return a {@link CompilationUnitChange}, or <code>null</code> for an empty change | |
230 | * @throws CoreException when text buffer acquisition or import rewrite text edit creation fails | |
231 | * @throws IllegalArgumentException when the AST rewrite encounters problems | |
232 | */ | |
233 | public CompilationUnitChange createChange(String name, boolean generateGroups, IProgressMonitor monitor) throws CoreException { | |
234 | CompilationUnitChange cuChange= new CompilationUnitChange(name, fCu); | |
235 | MultiTextEdit multiEdit= new MultiTextEdit(); | |
236 | cuChange.setEdit(multiEdit); | |
237 | return attachChange(cuChange, generateGroups, monitor); | |
238 | } | |
239 | ||
240 | ||
241 | /** | |
242 | * Attaches the changes of this compilation unit rewrite to the given CU Change. The given | |
243 | * change <b>must</b> either have no root edit, or a MultiTextEdit as a root edit. | |
244 | * The edits in the given change <b>must not</b> overlap with the changes of | |
245 | * this compilation unit. | |
246 | * | |
247 | * @param cuChange existing CompilationUnitChange with a MultiTextEdit root or no root at all. | |
248 | * @param generateGroups <code>true</code> to generate text edit groups, <code>false</code> otherwise | |
249 | * @param monitor the progress monitor or <code>null</code> | |
250 | * @return a change combining the changes of this rewrite and the given rewrite, or <code>null</code> for an empty change | |
251 | * @throws CoreException when text buffer acquisition or import rewrite text edit creation fails | |
252 | */ | |
253 | public CompilationUnitChange attachChange(CompilationUnitChange cuChange, boolean generateGroups, IProgressMonitor monitor) throws CoreException { | |
254 | try { | |
255 | boolean needsAstRewrite= fRewrite != null; // TODO: do we need something like ASTRewrite#hasChanges() here? | |
256 | boolean needsImportRemoval= fImportRemover != null && fImportRemover.hasRemovedNodes(); | |
257 | boolean needsImportRewrite= fImportRewrite != null && fImportRewrite.hasRecordedChanges() || needsImportRemoval; | |
258 | if (!needsAstRewrite && !needsImportRemoval && !needsImportRewrite) | |
259 | return null; | |
260 | ||
261 | MultiTextEdit multiEdit= (MultiTextEdit) cuChange.getEdit(); | |
262 | if (multiEdit == null) { | |
263 | multiEdit= new MultiTextEdit(); | |
264 | cuChange.setEdit(multiEdit); | |
265 | } | |
266 | ||
267 | if (needsAstRewrite) { | |
268 | TextEdit rewriteEdit; | |
269 | if (fRememberContent != null) { | |
270 | rewriteEdit= fRewrite.rewriteAST(fRememberContent, fCu.getJavaProject().getOptions(true)); | |
271 | } else { | |
272 | rewriteEdit= fRewrite.rewriteAST(); | |
273 | } | |
274 | if (!isEmptyEdit(rewriteEdit)) { | |
275 | multiEdit.addChild(rewriteEdit); | |
276 | if (generateGroups) { | |
277 | for (Iterator<TextEditGroup> iter= fTextEditGroups.iterator(); iter.hasNext();) { | |
278 | TextEditGroup group= iter.next(); | |
279 | cuChange.addTextEditGroup(group); | |
280 | } | |
281 | } | |
282 | } | |
283 | } | |
284 | if (needsImportRemoval) { | |
285 | fImportRemover.applyRemoves(getImportRewrite()); | |
286 | } | |
287 | if (needsImportRewrite) { | |
288 | TextEdit importsEdit= fImportRewrite.rewriteImports(monitor); | |
289 | if (!isEmptyEdit(importsEdit)) { | |
290 | multiEdit.addChild(importsEdit); | |
291 | String importUpdateName= RefactoringCoreMessages.ASTData_update_imports; | |
292 | cuChange.addTextEditGroup(new TextEditGroup(importUpdateName, importsEdit)); | |
293 | } | |
294 | } else { | |
295 | ||
296 | } | |
297 | if (isEmptyEdit(multiEdit)) | |
298 | return null; | |
299 | return cuChange; | |
300 | } finally { | |
301 | if (monitor != null) | |
302 | monitor.done(); | |
303 | } | |
304 | } | |
305 | ||
306 | private static boolean isEmptyEdit(TextEdit edit) { | |
307 | return edit.getClass() == MultiTextEdit.class && ! edit.hasChildren(); | |
308 | } | |
309 | ||
310 | public ICompilationUnit getCu() { | |
311 | return fCu; | |
312 | } | |
313 | ||
314 | public CompilationUnit getRoot() { | |
315 | if (fRoot == null) | |
316 | fRoot= new RefactoringASTParser(ASTProvider.SHARED_AST_LEVEL).parse(fCu, fOwner, fResolveBindings, fStatementsRecovery, fBindingsRecovery, null); | |
317 | return fRoot; | |
318 | } | |
319 | ||
320 | public AST getAST() { | |
321 | return getRoot().getAST(); | |
322 | } | |
323 | ||
324 | public ASTRewrite getASTRewrite() { | |
325 | if (fRewrite == null) { | |
326 | fRewrite= ASTRewrite.create(getRoot().getAST()); | |
327 | if (fRememberContent != null) { // wain until ast rewrite is accessed first | |
328 | try { | |
329 | fRememberContent.set(fCu.getSource()); | |
330 | } catch (JavaModelException e) { | |
331 | fRememberContent= null; | |
332 | } | |
333 | } | |
334 | } | |
335 | return fRewrite; | |
336 | } | |
337 | ||
338 | public ImportRewrite getImportRewrite() { | |
339 | if (fImportRewrite == null) { | |
340 | // lazily initialized to avoid lengthy processing in checkInitialConditions(..) | |
341 | try { | |
342 | /* If bindings are to be resolved, then create the AST, so that | |
343 | * ImportRewrite#setUseContextToFilterImplicitImports(boolean) will be set to true | |
344 | * and ContextSensitiveImportRewriteContext etc. can be used. */ | |
345 | if (fRoot == null && ! fResolveBindings) { | |
346 | fImportRewrite= StubUtility.createImportRewrite(fCu, true); | |
347 | } else { | |
348 | fImportRewrite= StubUtility.createImportRewrite(getRoot(), true); | |
349 | } | |
350 | } catch (CoreException e) { | |
351 | JavaPlugin.log(e); | |
352 | throw new IllegalStateException(e.getMessage()); // like ASTParser#createAST(..) does | |
353 | } | |
354 | } | |
355 | return fImportRewrite; | |
356 | ||
357 | } | |
358 | ||
359 | public ImportRemover getImportRemover() { | |
360 | if (fImportRemover == null) { | |
361 | fImportRemover= new ImportRemover(fCu.getJavaProject(), getRoot()); | |
362 | } | |
363 | return fImportRemover; | |
364 | } | |
365 | ||
366 | public void clearGroupDescriptions() { | |
367 | for (Iterator<TextEditGroup> iter= fTextEditGroups.iterator(); iter.hasNext();) { | |
368 | TextEditGroup group= iter.next(); | |
369 | group.clearTextEdits(); | |
370 | } | |
371 | } | |
372 | } |