]>
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.fix; | |
12 | ||
13 | import java.util.ArrayList; | |
14 | import java.util.List; | |
15 | ||
16 | import org.eclipse.core.runtime.CoreException; | |
17 | ||
18 | import org.eclipse.text.edits.TextEditGroup; | |
19 | ||
20 | import org.eclipse.jdt.core.dom.ASTNode; | |
21 | import org.eclipse.jdt.core.dom.Block; | |
22 | import org.eclipse.jdt.core.dom.ChildPropertyDescriptor; | |
23 | import org.eclipse.jdt.core.dom.CompilationUnit; | |
24 | import org.eclipse.jdt.core.dom.DoStatement; | |
25 | import org.eclipse.jdt.core.dom.EnhancedForStatement; | |
26 | import org.eclipse.jdt.core.dom.ForStatement; | |
27 | import org.eclipse.jdt.core.dom.IfStatement; | |
28 | import org.eclipse.jdt.core.dom.ReturnStatement; | |
29 | import org.eclipse.jdt.core.dom.Statement; | |
30 | import org.eclipse.jdt.core.dom.ThrowStatement; | |
31 | import org.eclipse.jdt.core.dom.WhileStatement; | |
32 | import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; | |
33 | ||
34 | import org.eclipse.jdt.internal.corext.dom.GenericVisitor; | |
35 | import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; | |
36 | ||
37 | import org.eclipse.jdt.ui.cleanup.ICleanUpFix; | |
38 | ||
39 | ||
40 | public class ControlStatementsFix extends CompilationUnitRewriteOperationsFix { | |
41 | ||
42 | private final static class ControlStatementFinder extends GenericVisitor { | |
43 | ||
44 | private final List<CompilationUnitRewriteOperation> fResult; | |
45 | private final boolean fFindControlStatementsWithoutBlock; | |
46 | private final boolean fRemoveUnnecessaryBlocks; | |
47 | private final boolean fRemoveUnnecessaryBlocksOnlyWhenReturnOrThrow; | |
48 | ||
49 | public ControlStatementFinder(boolean findControlStatementsWithoutBlock, | |
50 | boolean removeUnnecessaryBlocks, | |
51 | boolean removeUnnecessaryBlocksOnlyWhenReturnOrThrow, | |
52 | List<CompilationUnitRewriteOperation> resultingCollection) { | |
53 | ||
54 | fFindControlStatementsWithoutBlock= findControlStatementsWithoutBlock; | |
55 | fRemoveUnnecessaryBlocks= removeUnnecessaryBlocks; | |
56 | fRemoveUnnecessaryBlocksOnlyWhenReturnOrThrow= removeUnnecessaryBlocksOnlyWhenReturnOrThrow; | |
57 | fResult= resultingCollection; | |
58 | } | |
59 | ||
60 | /* (non-Javadoc) | |
61 | * @see org.eclipse.jdt.internal.corext.dom.GenericVisitor#visit(org.eclipse.jdt.core.dom.DoStatement) | |
62 | */ | |
63 | @Override | |
64 | public boolean visit(DoStatement node) { | |
65 | handle(node.getBody(), DoStatement.BODY_PROPERTY); | |
66 | ||
67 | return super.visit(node); | |
68 | } | |
69 | ||
70 | /* (non-Javadoc) | |
71 | * @see org.eclipse.jdt.internal.corext.dom.GenericVisitor#visit(org.eclipse.jdt.core.dom.ForStatement) | |
72 | */ | |
73 | @Override | |
74 | public boolean visit(ForStatement node) { | |
75 | handle(node.getBody(), ForStatement.BODY_PROPERTY); | |
76 | ||
77 | return super.visit(node); | |
78 | } | |
79 | ||
80 | /** | |
81 | * {@inheritDoc} | |
82 | */ | |
83 | @Override | |
84 | public boolean visit(EnhancedForStatement node) { | |
85 | handle(node.getBody(), EnhancedForStatement.BODY_PROPERTY); | |
86 | ||
87 | return super.visit(node); | |
88 | } | |
89 | ||
90 | /* (non-Javadoc) | |
91 | * @see org.eclipse.jdt.internal.corext.dom.GenericVisitor#visit(org.eclipse.jdt.core.dom.IfStatement) | |
92 | */ | |
93 | @Override | |
94 | public boolean visit(IfStatement statement) { | |
95 | handle(statement.getThenStatement(), IfStatement.THEN_STATEMENT_PROPERTY); | |
96 | ||
97 | Statement elseStatement= statement.getElseStatement(); | |
98 | if (elseStatement != null && !(elseStatement instanceof IfStatement)) { | |
99 | handle(elseStatement, IfStatement.ELSE_STATEMENT_PROPERTY); | |
100 | } | |
101 | ||
102 | return super.visit(statement); | |
103 | } | |
104 | ||
105 | /* (non-Javadoc) | |
106 | * @see org.eclipse.jdt.internal.corext.dom.GenericVisitor#visit(org.eclipse.jdt.core.dom.WhileStatement) | |
107 | */ | |
108 | @Override | |
109 | public boolean visit(WhileStatement node) { | |
110 | handle(node.getBody(), WhileStatement.BODY_PROPERTY); | |
111 | ||
112 | return super.visit(node); | |
113 | } | |
114 | ||
115 | private void handle(Statement body, ChildPropertyDescriptor bodyProperty) { | |
116 | if ((body.getFlags() & ASTNode.RECOVERED) != 0) | |
117 | return; | |
118 | Statement parent= (Statement)body.getParent(); | |
119 | if ((parent.getFlags() & ASTNode.RECOVERED) != 0) | |
120 | return; | |
121 | ||
122 | if (fRemoveUnnecessaryBlocksOnlyWhenReturnOrThrow) { | |
123 | if (!(body instanceof Block)) { | |
124 | if (body.getNodeType() != ASTNode.IF_STATEMENT && body.getNodeType() != ASTNode.RETURN_STATEMENT && body.getNodeType() != ASTNode.THROW_STATEMENT) { | |
125 | fResult.add(new AddBlockOperation(bodyProperty, body, parent)); | |
126 | } | |
127 | } else { | |
128 | if (RemoveBlockOperation.satisfiesCleanUpPrecondition(parent, bodyProperty, true)) { | |
129 | fResult.add(new RemoveBlockOperation(parent, bodyProperty)); | |
130 | } | |
131 | } | |
132 | } else if (fFindControlStatementsWithoutBlock) { | |
133 | if (!(body instanceof Block)) { | |
134 | fResult.add(new AddBlockOperation(bodyProperty, body, parent)); | |
135 | } | |
136 | } else if (fRemoveUnnecessaryBlocks) { | |
137 | if (RemoveBlockOperation.satisfiesCleanUpPrecondition(parent, bodyProperty, false)) { | |
138 | fResult.add(new RemoveBlockOperation(parent, bodyProperty)); | |
139 | } | |
140 | } | |
141 | } | |
142 | ||
143 | } | |
144 | ||
145 | private static class IfElseIterator { | |
146 | ||
147 | private IfStatement fCursor; | |
148 | ||
149 | public IfElseIterator(IfStatement item) { | |
150 | fCursor= findStart(item); | |
151 | } | |
152 | ||
153 | public IfStatement next() { | |
154 | if (!hasNext()) | |
155 | return null; | |
156 | ||
157 | IfStatement result= fCursor; | |
158 | ||
159 | if (fCursor.getElseStatement() instanceof IfStatement) { | |
160 | fCursor= (IfStatement)fCursor.getElseStatement(); | |
161 | } else { | |
162 | fCursor= null; | |
163 | } | |
164 | ||
165 | return result; | |
166 | } | |
167 | ||
168 | public boolean hasNext() { | |
169 | return fCursor != null; | |
170 | } | |
171 | ||
172 | private IfStatement findStart(IfStatement item) { | |
173 | while (item.getLocationInParent() == IfStatement.ELSE_STATEMENT_PROPERTY) { | |
174 | item= (IfStatement)item.getParent(); | |
175 | } | |
176 | return item; | |
177 | } | |
178 | } | |
179 | ||
180 | private static final class AddBlockOperation extends CompilationUnitRewriteOperation { | |
181 | ||
182 | private final ChildPropertyDescriptor fBodyProperty; | |
183 | private final Statement fBody; | |
184 | private final Statement fControlStatement; | |
185 | ||
186 | public AddBlockOperation(ChildPropertyDescriptor bodyProperty, Statement body, Statement controlStatement) { | |
187 | fBodyProperty= bodyProperty; | |
188 | fBody= body; | |
189 | fControlStatement= controlStatement; | |
190 | } | |
191 | ||
192 | /** | |
193 | * {@inheritDoc} | |
194 | */ | |
195 | @Override | |
196 | public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model) throws CoreException { | |
197 | ASTRewrite rewrite= cuRewrite.getASTRewrite(); | |
198 | String label; | |
199 | if (fBodyProperty == IfStatement.THEN_STATEMENT_PROPERTY) { | |
200 | label = FixMessages.CodeStyleFix_ChangeIfToBlock_desription; | |
201 | } else if (fBodyProperty == IfStatement.ELSE_STATEMENT_PROPERTY) { | |
202 | label = FixMessages.CodeStyleFix_ChangeElseToBlock_description; | |
203 | } else { | |
204 | label = FixMessages.CodeStyleFix_ChangeControlToBlock_description; | |
205 | } | |
206 | ||
207 | TextEditGroup group= createTextEditGroup(label, cuRewrite); | |
208 | ASTNode moveTarget= rewrite.createMoveTarget(fBody); | |
209 | Block replacingBody= cuRewrite.getRoot().getAST().newBlock(); | |
210 | replacingBody.statements().add(moveTarget); | |
211 | rewrite.set(fControlStatement, fBodyProperty, replacingBody, group); | |
212 | } | |
213 | ||
214 | } | |
215 | ||
216 | static class RemoveBlockOperation extends CompilationUnitRewriteOperation { | |
217 | ||
218 | private final Statement fStatement; | |
219 | private final ChildPropertyDescriptor fChild; | |
220 | ||
221 | public RemoveBlockOperation(Statement controlStatement, ChildPropertyDescriptor child) { | |
222 | fStatement= controlStatement; | |
223 | fChild= child; | |
224 | } | |
225 | ||
226 | /** | |
227 | * {@inheritDoc} | |
228 | */ | |
229 | @Override | |
230 | public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model) throws CoreException { | |
231 | ASTRewrite rewrite= cuRewrite.getASTRewrite(); | |
232 | ||
233 | Block block= (Block)fStatement.getStructuralProperty(fChild); | |
234 | Statement statement= (Statement)block.statements().get(0); | |
235 | Statement moveTarget= (Statement)rewrite.createMoveTarget(statement); | |
236 | ||
237 | TextEditGroup group= createTextEditGroup(FixMessages.ControlStatementsFix_removeBrackets_proposalDescription, cuRewrite); | |
238 | rewrite.set(fStatement, fChild, moveTarget, group); | |
239 | } | |
240 | ||
241 | public static boolean satisfiesCleanUpPrecondition(Statement controlStatement, ChildPropertyDescriptor childDescriptor, boolean onlyReturnAndThrows) { | |
242 | return satisfiesPrecondition(controlStatement, childDescriptor, onlyReturnAndThrows, true); | |
243 | } | |
244 | ||
245 | public static boolean satisfiesQuickAssistPrecondition(Statement controlStatement, ChildPropertyDescriptor childDescriptor) { | |
246 | return satisfiesPrecondition(controlStatement, childDescriptor, false, false); | |
247 | } | |
248 | ||
249 | //Can the block around child with childDescriptor of controlStatement be removed? | |
250 | private static boolean satisfiesPrecondition(Statement controlStatement, ChildPropertyDescriptor childDescriptor, boolean onlyReturnAndThrows, boolean cleanUpCheck) { | |
251 | Object child= controlStatement.getStructuralProperty(childDescriptor); | |
252 | ||
253 | if (!(child instanceof Block)) | |
254 | return false; | |
255 | ||
256 | Block block= (Block)child; | |
257 | List<Statement> list= block.statements(); | |
258 | if (list.size() != 1) | |
259 | return false; | |
260 | ||
261 | ASTNode singleStatement= list.get(0); | |
262 | ||
263 | if (onlyReturnAndThrows) | |
264 | if (!(singleStatement instanceof ReturnStatement) && !(singleStatement instanceof ThrowStatement)) | |
265 | return false; | |
266 | ||
267 | if (controlStatement instanceof IfStatement) { | |
268 | // if (true) { | |
269 | // while (true) | |
270 | // if (false) | |
271 | // ; | |
272 | // } else | |
273 | // ; | |
274 | ||
275 | if (((IfStatement)controlStatement).getThenStatement() != child) | |
276 | return true;//can always remove blocks in else part | |
277 | ||
278 | IfStatement ifStatement= (IfStatement)controlStatement; | |
279 | if (ifStatement.getElseStatement() == null) | |
280 | return true;//can always remove if no else part | |
281 | ||
282 | return !hasUnblockedIf((Statement)singleStatement, onlyReturnAndThrows, cleanUpCheck); | |
283 | } else { | |
284 | //if (true) | |
285 | // while (true) { | |
286 | // if (false) | |
287 | // ; | |
288 | // } | |
289 | //else | |
290 | // ; | |
291 | if (!hasUnblockedIf((Statement)singleStatement, onlyReturnAndThrows, cleanUpCheck)) | |
292 | return true; | |
293 | ||
294 | ASTNode currentChild= controlStatement; | |
295 | ASTNode parent= currentChild.getParent(); | |
296 | while (true) { | |
297 | Statement body= null; | |
298 | if (parent instanceof IfStatement) { | |
299 | body= ((IfStatement)parent).getThenStatement(); | |
300 | if (body == currentChild && ((IfStatement)parent).getElseStatement() != null)//->currentChild is an unblocked then part | |
301 | return false; | |
302 | } else if (parent instanceof WhileStatement) { | |
303 | body= ((WhileStatement)parent).getBody(); | |
304 | } else if (parent instanceof DoStatement) { | |
305 | body= ((DoStatement)parent).getBody(); | |
306 | } else if (parent instanceof ForStatement) { | |
307 | body= ((ForStatement)parent).getBody(); | |
308 | } else if (parent instanceof EnhancedForStatement) { | |
309 | body= ((EnhancedForStatement)parent).getBody(); | |
310 | } else { | |
311 | return true; | |
312 | } | |
313 | if (body != currentChild)//->parents child is a block | |
314 | return true; | |
315 | ||
316 | currentChild= parent; | |
317 | parent= currentChild.getParent(); | |
318 | } | |
319 | } | |
320 | } | |
321 | ||
322 | private static boolean hasUnblockedIf(Statement p, boolean onlyReturnAndThrows, boolean cleanUpCheck) { | |
323 | while (true) { | |
324 | if (p instanceof IfStatement) { | |
325 | return true; | |
326 | } else { | |
327 | ||
328 | ChildPropertyDescriptor childD= null; | |
329 | if (p instanceof WhileStatement) { | |
330 | childD= WhileStatement.BODY_PROPERTY; | |
331 | } else if (p instanceof ForStatement) { | |
332 | childD= ForStatement.BODY_PROPERTY; | |
333 | } else if (p instanceof EnhancedForStatement) { | |
334 | childD= EnhancedForStatement.BODY_PROPERTY; | |
335 | } else if (p instanceof DoStatement) { | |
336 | childD= DoStatement.BODY_PROPERTY; | |
337 | } else { | |
338 | return false; | |
339 | } | |
340 | Statement body= (Statement)p.getStructuralProperty(childD); | |
341 | if (body instanceof Block) { | |
342 | if (!cleanUpCheck) { | |
343 | return false; | |
344 | } else { | |
345 | if (!satisfiesPrecondition(p, childD, onlyReturnAndThrows, cleanUpCheck)) | |
346 | return false; | |
347 | ||
348 | p= (Statement)((Block)body).statements().get(0); | |
349 | } | |
350 | } else { | |
351 | p= body; | |
352 | } | |
353 | } | |
354 | } | |
355 | } | |
356 | ||
357 | } | |
358 | ||
359 | public static ControlStatementsFix[] createRemoveBlockFix(CompilationUnit compilationUnit, ASTNode node) { | |
360 | if (!(node instanceof Statement)) { | |
361 | return null; | |
362 | } | |
363 | Statement statement= (Statement) node; | |
364 | ||
365 | if (statement instanceof Block) { | |
366 | Block block= (Block)statement; | |
367 | if (block.statements().size() != 1) | |
368 | return null; | |
369 | ||
370 | ASTNode parent= block.getParent(); | |
371 | if (!(parent instanceof Statement)) | |
372 | return null; | |
373 | ||
374 | statement= (Statement)parent; | |
375 | } | |
376 | ||
377 | if (statement instanceof IfStatement) { | |
378 | List<ControlStatementsFix> result= new ArrayList<ControlStatementsFix>(); | |
379 | ||
380 | List<RemoveBlockOperation> removeAllList= new ArrayList<RemoveBlockOperation>(); | |
381 | ||
382 | IfElseIterator iter= new IfElseIterator((IfStatement)statement); | |
383 | IfStatement item= null; | |
384 | while (iter.hasNext()) { | |
385 | item= iter.next(); | |
386 | if (RemoveBlockOperation.satisfiesQuickAssistPrecondition(item, IfStatement.THEN_STATEMENT_PROPERTY)) { | |
387 | RemoveBlockOperation op= new RemoveBlockOperation(item, IfStatement.THEN_STATEMENT_PROPERTY); | |
388 | removeAllList.add(op); | |
389 | if (item == statement) | |
390 | result.add(new ControlStatementsFix(FixMessages.ControlStatementsFix_removeIfBlock_proposalDescription, compilationUnit, new CompilationUnitRewriteOperation[] {op})); | |
391 | } | |
392 | } | |
393 | ||
394 | if (RemoveBlockOperation.satisfiesQuickAssistPrecondition(item, IfStatement.ELSE_STATEMENT_PROPERTY)) { | |
395 | RemoveBlockOperation op= new RemoveBlockOperation(item, IfStatement.ELSE_STATEMENT_PROPERTY); | |
396 | removeAllList.add(op); | |
397 | if (item == statement) | |
398 | result.add(new ControlStatementsFix(FixMessages.ControlStatementsFix_removeElseBlock_proposalDescription, compilationUnit, new CompilationUnitRewriteOperation[] {op})); | |
399 | } | |
400 | ||
401 | if (removeAllList.size() > 1) { | |
402 | CompilationUnitRewriteOperation[] allConvert= removeAllList.toArray(new CompilationUnitRewriteOperation[removeAllList.size()]); | |
403 | result.add(new ControlStatementsFix(FixMessages.ControlStatementsFix_removeIfElseBlock_proposalDescription, compilationUnit, allConvert)); | |
404 | } | |
405 | ||
406 | return result.toArray(new ControlStatementsFix[result.size()]); | |
407 | } else if (statement instanceof WhileStatement) { | |
408 | if (RemoveBlockOperation.satisfiesQuickAssistPrecondition(statement, WhileStatement.BODY_PROPERTY)) { | |
409 | RemoveBlockOperation op= new RemoveBlockOperation(statement, WhileStatement.BODY_PROPERTY); | |
410 | return new ControlStatementsFix[] {new ControlStatementsFix(FixMessages.ControlStatementsFix_removeBrackets_proposalDescription, compilationUnit, new CompilationUnitRewriteOperation[] {op})}; | |
411 | } | |
412 | } else if (statement instanceof ForStatement) { | |
413 | if (RemoveBlockOperation.satisfiesQuickAssistPrecondition(statement, ForStatement.BODY_PROPERTY)) { | |
414 | RemoveBlockOperation op= new RemoveBlockOperation(statement, ForStatement.BODY_PROPERTY); | |
415 | return new ControlStatementsFix[] {new ControlStatementsFix(FixMessages.ControlStatementsFix_removeBrackets_proposalDescription, compilationUnit, new CompilationUnitRewriteOperation[] {op})}; | |
416 | } | |
417 | } else if (statement instanceof EnhancedForStatement) { | |
418 | if (RemoveBlockOperation.satisfiesQuickAssistPrecondition(statement, EnhancedForStatement.BODY_PROPERTY)) { | |
419 | RemoveBlockOperation op= new RemoveBlockOperation(statement, EnhancedForStatement.BODY_PROPERTY); | |
420 | return new ControlStatementsFix[] {new ControlStatementsFix(FixMessages.ControlStatementsFix_removeBrackets_proposalDescription, compilationUnit, new CompilationUnitRewriteOperation[] {op})}; | |
421 | } | |
422 | } else if (statement instanceof DoStatement) { | |
423 | if (RemoveBlockOperation.satisfiesQuickAssistPrecondition(statement, DoStatement.BODY_PROPERTY)) { | |
424 | RemoveBlockOperation op= new RemoveBlockOperation(statement, DoStatement.BODY_PROPERTY); | |
425 | return new ControlStatementsFix[] {new ControlStatementsFix(FixMessages.ControlStatementsFix_removeBrackets_proposalDescription, compilationUnit, new CompilationUnitRewriteOperation[] {op})}; | |
426 | } | |
427 | } | |
428 | ||
429 | return null; | |
430 | } | |
431 | ||
432 | public static ICleanUpFix createCleanUp(CompilationUnit compilationUnit, | |
433 | boolean convertSingleStatementToBlock, | |
434 | boolean removeUnnecessaryBlock, | |
435 | boolean removeUnnecessaryBlockContainingReturnOrThrow) { | |
436 | ||
437 | if (!convertSingleStatementToBlock && !removeUnnecessaryBlock && !removeUnnecessaryBlockContainingReturnOrThrow) | |
438 | return null; | |
439 | ||
440 | List<CompilationUnitRewriteOperation> operations= new ArrayList<CompilationUnitRewriteOperation>(); | |
441 | ControlStatementFinder finder= new ControlStatementFinder(convertSingleStatementToBlock, removeUnnecessaryBlock, removeUnnecessaryBlockContainingReturnOrThrow, operations); | |
442 | compilationUnit.accept(finder); | |
443 | ||
444 | if (operations.isEmpty()) | |
445 | return null; | |
446 | ||
447 | CompilationUnitRewriteOperation[] ops= operations.toArray(new CompilationUnitRewriteOperation[operations.size()]); | |
448 | return new ControlStatementsFix(FixMessages.ControlStatementsFix_change_name, compilationUnit, ops); | |
449 | } | |
450 | ||
451 | protected ControlStatementsFix(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation[] fixRewriteOperations) { | |
452 | super(name, compilationUnit, fixRewriteOperations); | |
453 | } | |
454 | ||
455 | } |