1 /******************************************************************************* 2 * Copyright (c) 2000, 2019 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * Konstantin Scheglov (scheglov_ke@nlmk.ru) - initial API and implementation 13 * (reports 71244 & 74746: New Quick Assist's [quick assist]) 14 * IBM Corporation - implementation 15 * Billy Huang <billyhuang31@gmail.com> - [quick assist] concatenate/merge string literals - https://bugs.eclipse.org/77632 16 *******************************************************************************/ 17 package org.eclipse.jdt.internal.ui.text.correction; 18 19 import java.util.ArrayList; 20 import java.util.Arrays; 21 import java.util.Collection; 22 import java.util.HashSet; 23 import java.util.Hashtable; 24 import java.util.Iterator; 25 import java.util.List; 26 import java.util.ListIterator; 27 import java.util.Map; 28 29 import org.eclipse.swt.graphics.Image; 30 31 import org.eclipse.core.runtime.CoreException; 32 33 import org.eclipse.jdt.core.ICompilationUnit; 34 import org.eclipse.jdt.core.IJavaProject; 35 import org.eclipse.jdt.core.NamingConventions; 36 import org.eclipse.jdt.core.dom.AST; 37 import org.eclipse.jdt.core.dom.ASTMatcher; 38 import org.eclipse.jdt.core.dom.ASTNode; 39 import org.eclipse.jdt.core.dom.ASTVisitor; 40 import org.eclipse.jdt.core.dom.AssertStatement; 41 import org.eclipse.jdt.core.dom.Assignment; 42 import org.eclipse.jdt.core.dom.Block; 43 import org.eclipse.jdt.core.dom.BodyDeclaration; 44 import org.eclipse.jdt.core.dom.BooleanLiteral; 45 import org.eclipse.jdt.core.dom.BreakStatement; 46 import org.eclipse.jdt.core.dom.CastExpression; 47 import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor; 48 import org.eclipse.jdt.core.dom.ClassInstanceCreation; 49 import org.eclipse.jdt.core.dom.CompilationUnit; 50 import org.eclipse.jdt.core.dom.ConditionalExpression; 51 import org.eclipse.jdt.core.dom.ConstructorInvocation; 52 import org.eclipse.jdt.core.dom.ContinueStatement; 53 import org.eclipse.jdt.core.dom.DoStatement; 54 import org.eclipse.jdt.core.dom.EnhancedForStatement; 55 import org.eclipse.jdt.core.dom.EnumConstantDeclaration; 56 import org.eclipse.jdt.core.dom.Expression; 57 import org.eclipse.jdt.core.dom.ExpressionStatement; 58 import org.eclipse.jdt.core.dom.ForStatement; 59 import org.eclipse.jdt.core.dom.IBinding; 60 import org.eclipse.jdt.core.dom.IMethodBinding; 61 import org.eclipse.jdt.core.dom.ITypeBinding; 62 import org.eclipse.jdt.core.dom.IVariableBinding; 63 import org.eclipse.jdt.core.dom.IfStatement; 64 import org.eclipse.jdt.core.dom.InfixExpression; 65 import org.eclipse.jdt.core.dom.InfixExpression.Operator; 66 import org.eclipse.jdt.core.dom.InstanceofExpression; 67 import org.eclipse.jdt.core.dom.LambdaExpression; 68 import org.eclipse.jdt.core.dom.MethodDeclaration; 69 import org.eclipse.jdt.core.dom.MethodInvocation; 70 import org.eclipse.jdt.core.dom.Modifier; 71 import org.eclipse.jdt.core.dom.Name; 72 import org.eclipse.jdt.core.dom.ParenthesizedExpression; 73 import org.eclipse.jdt.core.dom.PrefixExpression; 74 import org.eclipse.jdt.core.dom.PrimitiveType; 75 import org.eclipse.jdt.core.dom.QualifiedName; 76 import org.eclipse.jdt.core.dom.ReturnStatement; 77 import org.eclipse.jdt.core.dom.SimpleName; 78 import org.eclipse.jdt.core.dom.SingleVariableDeclaration; 79 import org.eclipse.jdt.core.dom.Statement; 80 import org.eclipse.jdt.core.dom.StringLiteral; 81 import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor; 82 import org.eclipse.jdt.core.dom.SuperConstructorInvocation; 83 import org.eclipse.jdt.core.dom.SuperMethodInvocation; 84 import org.eclipse.jdt.core.dom.SwitchCase; 85 import org.eclipse.jdt.core.dom.SwitchStatement; 86 import org.eclipse.jdt.core.dom.ThrowStatement; 87 import org.eclipse.jdt.core.dom.Type; 88 import org.eclipse.jdt.core.dom.VariableDeclarationFragment; 89 import org.eclipse.jdt.core.dom.VariableDeclarationStatement; 90 import org.eclipse.jdt.core.dom.WhileStatement; 91 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; 92 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; 93 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext; 94 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.TypeLocation; 95 import org.eclipse.jdt.core.dom.rewrite.ListRewrite; 96 97 import org.eclipse.jdt.internal.core.manipulation.StubUtility; 98 import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving; 99 import org.eclipse.jdt.internal.core.manipulation.dom.NecessaryParenthesesChecker; 100 import org.eclipse.jdt.internal.core.manipulation.dom.OperatorPrecedence; 101 import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext; 102 import org.eclipse.jdt.internal.corext.dom.ASTNodes; 103 import org.eclipse.jdt.internal.corext.dom.GenericVisitor; 104 import org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder; 105 import org.eclipse.jdt.internal.corext.dom.StatementRewrite; 106 import org.eclipse.jdt.internal.corext.fix.CleanUpConstants; 107 import org.eclipse.jdt.internal.corext.fix.ExpressionsFix; 108 import org.eclipse.jdt.internal.corext.fix.IProposableFix; 109 import org.eclipse.jdt.internal.corext.refactoring.code.Invocations; 110 import org.eclipse.jdt.internal.corext.refactoring.util.NoCommentSourceRangeComputer; 111 import org.eclipse.jdt.internal.corext.refactoring.util.TightSourceRangeComputer; 112 import org.eclipse.jdt.internal.corext.util.JavaModelUtil; 113 import org.eclipse.jdt.internal.corext.util.Messages; 114 115 import org.eclipse.jdt.ui.cleanup.CleanUpOptions; 116 import org.eclipse.jdt.ui.text.java.IInvocationContext; 117 import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal; 118 import org.eclipse.jdt.ui.text.java.IProblemLocation; 119 import org.eclipse.jdt.ui.text.java.IQuickAssistProcessor; 120 import org.eclipse.jdt.ui.text.java.correction.ASTRewriteCorrectionProposal; 121 import org.eclipse.jdt.ui.text.java.correction.ICommandAccess; 122 123 import org.eclipse.jdt.internal.ui.JavaPluginImages; 124 import org.eclipse.jdt.internal.ui.fix.ExpressionsCleanUp; 125 import org.eclipse.jdt.internal.ui.text.correction.proposals.FixCorrectionProposal; 126 import org.eclipse.jdt.internal.ui.text.correction.proposals.LinkedCorrectionProposal; 127 import org.eclipse.jdt.internal.ui.util.ASTHelper; 128 129 /** 130 */ 131 public class AdvancedQuickAssistProcessor implements IQuickAssistProcessor { AdvancedQuickAssistProcessor()132 public AdvancedQuickAssistProcessor() { 133 super(); 134 } 135 136 @Override hasAssists(IInvocationContext context)137 public boolean hasAssists(IInvocationContext context) throws CoreException { 138 ASTNode coveringNode= context.getCoveringNode(); 139 if (coveringNode != null) { 140 ArrayList<ASTNode> coveredNodes= getFullyCoveredNodes(context, coveringNode); 141 return getConvertToIfReturnProposals(context, coveringNode, null) 142 || getInverseIfProposals(context, coveringNode, null) 143 || getIfReturnIntoIfElseAtEndOfVoidMethodProposals(context, coveringNode, null) 144 || getInverseIfContinueIntoIfThenInLoopsProposals(context, coveringNode, null) 145 || getInverseIfIntoContinueInLoopsProposals(context, coveringNode, null) 146 || getInverseConditionProposals(context, coveringNode, coveredNodes, null) 147 || getRemoveExtraParenthesesProposals(context, coveringNode, coveredNodes, null) 148 || getAddParanoidalParenthesesProposals(context, coveredNodes, null) 149 || getAddParenthesesForExpressionProposals(context, coveringNode, null) 150 || getJoinAndIfStatementsProposals(context, coveringNode, null) 151 || getSplitAndConditionProposals(context, coveringNode, null) 152 || getJoinOrIfStatementsProposals(context, coveringNode, coveredNodes, null) 153 || getSplitOrConditionProposals(context, coveringNode, null) 154 || getInverseConditionalExpressionProposals(context, coveringNode, null) 155 || getExchangeInnerAndOuterIfConditionsProposals(context, coveringNode, null) 156 || getExchangeOperandsProposals(context, coveringNode, null) 157 || getCastAndAssignIfStatementProposals(context, coveringNode, null) 158 || getCombineStringProposals(context, coveringNode, null) 159 || getPickOutStringProposals(context, coveringNode, null) 160 || getReplaceIfElseWithConditionalProposals(context, coveringNode, null) 161 || getReplaceConditionalWithIfElseProposals(context, coveringNode, null) 162 || getInverseLocalVariableProposals(context, coveringNode, null) 163 || getPushNegationDownProposals(context, coveringNode, null) 164 || getPullNegationUpProposals(context, coveredNodes, null) 165 || getJoinIfListInIfElseIfProposals(context, coveringNode, coveredNodes, null) 166 || getConvertSwitchToIfProposals(context, coveringNode, null) 167 || getConvertIfElseToSwitchProposals(context, coveringNode, null) 168 || GetterSetterCorrectionSubProcessor.addGetterSetterProposal(context, coveringNode, null, null); 169 } 170 return false; 171 } 172 173 @Override getAssists(IInvocationContext context, IProblemLocation[] locations)174 public IJavaCompletionProposal[] getAssists(IInvocationContext context, IProblemLocation[] locations) throws CoreException { 175 ASTNode coveringNode= context.getCoveringNode(); 176 if (coveringNode != null) { 177 ArrayList<ASTNode> coveredNodes= getFullyCoveredNodes(context, coveringNode); 178 ArrayList<ICommandAccess> resultingCollections= new ArrayList<>(); 179 180 //quick assists that show up also if there is an error/warning 181 getReplaceConditionalWithIfElseProposals(context, coveringNode, resultingCollections); 182 183 if (QuickAssistProcessor.noErrorsAtLocation(locations)) { 184 getConvertToIfReturnProposals(context, coveringNode, resultingCollections); 185 getInverseIfProposals(context, coveringNode, resultingCollections); 186 getIfReturnIntoIfElseAtEndOfVoidMethodProposals(context, coveringNode, resultingCollections); 187 getInverseIfContinueIntoIfThenInLoopsProposals(context, coveringNode, resultingCollections); 188 getInverseIfIntoContinueInLoopsProposals(context, coveringNode, resultingCollections); 189 getInverseConditionProposals(context, coveringNode, coveredNodes, resultingCollections); 190 getRemoveExtraParenthesesProposals(context, coveringNode, coveredNodes, resultingCollections); 191 getAddParanoidalParenthesesProposals(context, coveredNodes, resultingCollections); 192 getAddParenthesesForExpressionProposals(context, coveringNode, resultingCollections); 193 getJoinAndIfStatementsProposals(context, coveringNode, resultingCollections); 194 getSplitAndConditionProposals(context, coveringNode, resultingCollections); 195 getJoinOrIfStatementsProposals(context, coveringNode, coveredNodes, resultingCollections); 196 getSplitOrConditionProposals(context, coveringNode, resultingCollections); 197 getInverseConditionalExpressionProposals(context, coveringNode, resultingCollections); 198 getExchangeInnerAndOuterIfConditionsProposals(context, coveringNode, resultingCollections); 199 getExchangeOperandsProposals(context, coveringNode, resultingCollections); 200 getCastAndAssignIfStatementProposals(context, coveringNode, resultingCollections); 201 getCombineStringProposals(context, coveringNode, resultingCollections); 202 getPickOutStringProposals(context, coveringNode, resultingCollections); 203 getReplaceIfElseWithConditionalProposals(context, coveringNode, resultingCollections); 204 getInverseLocalVariableProposals(context, coveringNode, resultingCollections); 205 getPushNegationDownProposals(context, coveringNode, resultingCollections); 206 getPullNegationUpProposals(context, coveredNodes, resultingCollections); 207 getJoinIfListInIfElseIfProposals(context, coveringNode, coveredNodes, resultingCollections); 208 getConvertSwitchToIfProposals(context, coveringNode, resultingCollections); 209 getConvertIfElseToSwitchProposals(context, coveringNode, resultingCollections); 210 GetterSetterCorrectionSubProcessor.addGetterSetterProposal(context, coveringNode, locations, resultingCollections); 211 } 212 213 return resultingCollections.toArray(new IJavaCompletionProposal[resultingCollections.size()]); 214 } 215 return null; 216 } 217 getConvertToIfReturnProposals(IInvocationContext context, ASTNode coveringNode, ArrayList<ICommandAccess> resultingCollections)218 private static boolean getConvertToIfReturnProposals(IInvocationContext context, ASTNode coveringNode, ArrayList<ICommandAccess> resultingCollections) { 219 if (!(coveringNode instanceof IfStatement)) { 220 return false; 221 } 222 IfStatement ifStatement= (IfStatement) coveringNode; 223 if (ifStatement.getElseStatement() != null) { 224 return false; 225 } 226 227 // enclosing lambda or method should return 'void' 228 LambdaExpression enclosingLambda= ASTResolving.findEnclosingLambdaExpression(ifStatement); 229 if (enclosingLambda != null) { 230 IMethodBinding lambdaMethodBinding= enclosingLambda.resolveMethodBinding(); 231 if (lambdaMethodBinding == null) { 232 return false; 233 } 234 if (!(ifStatement.getAST().resolveWellKnownType("void").equals(lambdaMethodBinding.getReturnType()))) { //$NON-NLS-1$ 235 return false; 236 } 237 } else { 238 MethodDeclaration coveringMethod= ASTResolving.findParentMethodDeclaration(ifStatement); 239 if (coveringMethod == null) { 240 return false; 241 } 242 Type returnType= coveringMethod.getReturnType2(); 243 if (!isVoid(returnType)) { 244 return false; 245 } 246 } 247 248 // should be present in a block 249 if (!(ifStatement.getParent() instanceof Block)) { 250 return false; 251 } 252 // should have at least one statement in 'then' part other than 'return' 253 Statement thenStatement= ifStatement.getThenStatement(); 254 if (thenStatement instanceof ReturnStatement) { 255 return false; 256 } 257 if (thenStatement instanceof Block) { 258 List<Statement> thenStatements= ((Block) thenStatement).statements(); 259 if (thenStatements.isEmpty() || (thenStatements.size() == 1 && (thenStatements.get(0) instanceof ReturnStatement))) { 260 return false; 261 } 262 } 263 // should have no further executable statement 264 if (!isLastStatementInEnclosingMethodOrLambda(ifStatement)) { 265 return false; 266 } 267 // we could produce quick assist 268 if (resultingCollections == null) { 269 return true; 270 } 271 272 AST ast= coveringNode.getAST(); 273 ASTRewrite rewrite= ASTRewrite.create(ast); 274 275 // create inverted 'if' statement 276 Expression inversedExpression= getInversedExpression(rewrite, ifStatement.getExpression()); 277 IfStatement newIf= ast.newIfStatement(); 278 newIf.setExpression(inversedExpression); 279 newIf.setThenStatement(ast.newReturnStatement()); 280 ListRewrite listRewriter= rewrite.getListRewrite(ifStatement.getParent(), (ChildListPropertyDescriptor) ifStatement.getLocationInParent()); 281 listRewriter.replace(ifStatement, newIf, null); 282 // remove last 'return' in 'then' block 283 ArrayList<Statement> statements= getUnwrappedStatements(ifStatement.getThenStatement()); 284 Statement lastStatement= statements.get(statements.size() - 1); 285 if (lastStatement instanceof ReturnStatement) { 286 statements.remove(lastStatement); 287 } 288 // add statements from 'then' to the end of block 289 for (Statement statement : statements) { 290 listRewriter.insertLast(rewrite.createMoveTarget(statement), null); 291 } 292 293 // add correction proposal 294 String label= CorrectionMessages.AdvancedQuickAssistProcessor_convertToIfReturn; 295 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 296 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.CONVERT_TO_IF_RETURN, image); 297 resultingCollections.add(proposal); 298 return true; 299 } 300 isVoid(Type type)301 private static boolean isVoid(Type type) { 302 return type instanceof PrimitiveType && ((PrimitiveType) type).getPrimitiveTypeCode() == PrimitiveType.VOID; 303 } 304 isLastStatementInEnclosingMethodOrLambda(Statement statement)305 private static boolean isLastStatementInEnclosingMethodOrLambda(Statement statement) { 306 ASTNode currentStructure= statement; 307 ASTNode currentParent= statement.getParent(); 308 while (!(currentParent instanceof MethodDeclaration) && !(currentParent instanceof LambdaExpression)) { 309 // should not be in a loop 310 if (currentParent instanceof ForStatement || currentParent instanceof EnhancedForStatement 311 || currentParent instanceof WhileStatement || currentParent instanceof DoStatement) { 312 return false; 313 } 314 if (currentParent instanceof Block) { 315 Block parentBlock= (Block) currentParent; 316 if (parentBlock.statements().indexOf(currentStructure) != parentBlock.statements().size() - 1) { // not last statement in the block 317 return false; 318 } 319 } 320 currentStructure= currentParent; 321 currentParent= currentParent.getParent(); 322 } 323 return true; 324 } 325 getIfReturnIntoIfElseAtEndOfVoidMethodProposals(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections)326 private static boolean getIfReturnIntoIfElseAtEndOfVoidMethodProposals(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections) { 327 if (!(covering instanceof IfStatement)) { 328 return false; 329 } 330 IfStatement ifStatement= (IfStatement) covering; 331 if (ifStatement.getElseStatement() != null) { 332 return false; 333 } 334 // 'then' block should have 'return' as last statement 335 Statement thenStatement= ifStatement.getThenStatement(); 336 if (!(thenStatement instanceof Block)) { 337 return false; 338 } 339 Block thenBlock= (Block) thenStatement; 340 List<Statement> thenStatements= thenBlock.statements(); 341 if (thenStatements.isEmpty() || !(thenStatements.get(thenStatements.size() - 1) instanceof ReturnStatement)) { 342 return false; 343 } 344 // method should return 'void' 345 MethodDeclaration coveringMetod= ASTResolving.findParentMethodDeclaration(covering); 346 if (coveringMetod == null) { 347 return false; 348 } 349 Type returnType= coveringMetod.getReturnType2(); 350 if (!isVoid(returnType)) { 351 return false; 352 } 353 // 354 List<Statement> statements= coveringMetod.getBody().statements(); 355 int ifIndex= statements.indexOf(ifStatement); 356 if (ifIndex == -1) { 357 return false; 358 } 359 // we could produce quick assist 360 if (resultingCollections == null) { 361 return true; 362 } 363 // 364 AST ast= covering.getAST(); 365 ASTRewrite rewrite= ASTRewrite.create(ast); 366 // remove last 'return' in 'then' block 367 ListRewrite listRewriter= rewrite.getListRewrite(thenBlock, (ChildListPropertyDescriptor) ifStatement.getLocationInParent()); 368 listRewriter.remove(thenStatements.get(thenStatements.size() - 1), null); 369 // prepare original nodes 370 Expression conditionPlaceholder= (Expression) rewrite.createMoveTarget(ifStatement.getExpression()); 371 Statement thenPlaceholder= (Statement) rewrite.createMoveTarget(ifStatement.getThenStatement()); 372 // prepare 'else' block 373 Block elseBlock= ast.newBlock(); 374 for (int i= ifIndex + 1; i < statements.size(); i++) { 375 Statement statement= statements.get(i); 376 elseBlock.statements().add(rewrite.createMoveTarget(statement)); 377 } 378 // prepare new 'if' statement 379 IfStatement newIf= ast.newIfStatement(); 380 newIf.setExpression(conditionPlaceholder); 381 newIf.setThenStatement(thenPlaceholder); 382 newIf.setElseStatement(elseBlock); 383 rewrite.replace(ifStatement, newIf, null); 384 // add correction proposal 385 String label= CorrectionMessages.AdvancedQuickAssistProcessor_convertToIfElse_description; 386 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 387 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.CONVERT_TO_IF_ELSE, image); 388 resultingCollections.add(proposal); 389 return true; 390 } 391 getInverseIfProposals(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections)392 private static boolean getInverseIfProposals(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections) { 393 if (!(covering instanceof IfStatement)) { 394 return false; 395 } 396 IfStatement ifStatement= (IfStatement) covering; 397 if (ifStatement.getElseStatement() == null) { 398 return false; 399 } 400 // we could produce quick assist 401 if (resultingCollections == null) { 402 return true; 403 } 404 // 405 AST ast= covering.getAST(); 406 ASTRewrite rewrite= ASTRewrite.create(ast); 407 Statement thenStatement= ifStatement.getThenStatement(); 408 Statement elseStatement= ifStatement.getElseStatement(); 409 410 // prepare original nodes 411 Expression inversedExpression= getInversedExpression(rewrite, ifStatement.getExpression()); 412 413 Statement newElseStatement= (Statement) rewrite.createMoveTarget(thenStatement); 414 Statement newThenStatement= (Statement) rewrite.createMoveTarget(elseStatement); 415 // set new nodes 416 rewrite.set(ifStatement, IfStatement.EXPRESSION_PROPERTY, inversedExpression, null); 417 418 if (elseStatement instanceof IfStatement) {// bug 79507 && bug 74580 419 Block elseBlock= ast.newBlock(); 420 elseBlock.statements().add(newThenStatement); 421 newThenStatement= elseBlock; 422 } 423 rewrite.set(ifStatement, IfStatement.THEN_STATEMENT_PROPERTY, newThenStatement, null); 424 rewrite.set(ifStatement, IfStatement.ELSE_STATEMENT_PROPERTY, newElseStatement, null); 425 // add correction proposal 426 String label= CorrectionMessages.AdvancedQuickAssistProcessor_inverseIf_description; 427 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 428 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.INVERSE_IF_STATEMENT, image); 429 resultingCollections.add(proposal); 430 return true; 431 } 432 getInverseIfContinueIntoIfThenInLoopsProposals(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections)433 private static boolean getInverseIfContinueIntoIfThenInLoopsProposals(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections) { 434 if (!(covering instanceof IfStatement)) { 435 return false; 436 } 437 IfStatement ifStatement= (IfStatement) covering; 438 if (ifStatement.getElseStatement() != null) { 439 return false; 440 } 441 // check that 'then' is 'continue' 442 if (!(ifStatement.getThenStatement() instanceof ContinueStatement)) { 443 return false; 444 } 445 // check that 'if' statement is statement in block that is body of loop 446 Block loopBlock= null; 447 if (ifStatement.getParent() instanceof Block && ifStatement.getParent().getParent() instanceof ForStatement) { 448 loopBlock= (Block) ifStatement.getParent(); 449 } else if (ifStatement.getParent() instanceof Block && ifStatement.getParent().getParent() instanceof WhileStatement) { 450 loopBlock= (Block) ifStatement.getParent(); 451 } else { 452 return false; 453 } 454 if (resultingCollections == null) { 455 return true; 456 } 457 // 458 AST ast= covering.getAST(); 459 ASTRewrite rewrite= ASTRewrite.create(ast); 460 // create inverted 'if' statement 461 Expression inversedExpression= getInversedExpression(rewrite, ifStatement.getExpression()); 462 IfStatement newIf= ast.newIfStatement(); 463 newIf.setExpression(inversedExpression); 464 // prepare 'then' for new 'if' 465 Block thenBlock= ast.newBlock(); 466 int ifIndex= loopBlock.statements().indexOf(ifStatement); 467 for (int i= ifIndex + 1; i < loopBlock.statements().size(); i++) { 468 Statement statement= (Statement) loopBlock.statements().get(i); 469 thenBlock.statements().add(rewrite.createMoveTarget(statement)); 470 } 471 newIf.setThenStatement(thenBlock); 472 // replace 'if' statement in loop 473 rewrite.replace(ifStatement, newIf, null); 474 // add correction proposal 475 String label= CorrectionMessages.AdvancedQuickAssistProcessor_inverseIfContinue_description; 476 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 477 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.INVERSE_IF_CONTINUE, image); 478 resultingCollections.add(proposal); 479 return true; 480 } 481 getInverseIfIntoContinueInLoopsProposals(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections)482 private static boolean getInverseIfIntoContinueInLoopsProposals(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections) { 483 if (!(covering instanceof IfStatement)) { 484 return false; 485 } 486 IfStatement ifStatement= (IfStatement) covering; 487 if (ifStatement.getElseStatement() != null) { 488 return false; 489 } 490 // prepare outer control structure and block that contains 'if' statement 491 ASTNode ifParent= ifStatement.getParent(); 492 Block ifParentBlock= null; 493 ASTNode ifParentStructure= ifParent; 494 if (ifParentStructure instanceof Block) { 495 ifParentBlock= (Block) ifParent; 496 ifParentStructure= ifParentStructure.getParent(); 497 } 498 // check that control structure is loop and 'if' statement if last statement 499 if (!(ifParentStructure instanceof ForStatement) && !(ifParentStructure instanceof WhileStatement)) { 500 return false; 501 } 502 if (ifParentBlock != null && ifParentBlock.statements().indexOf(ifStatement) != ifParentBlock.statements().size() - 1) { 503 return false; 504 } 505 // we could produce quick assist 506 if (resultingCollections == null) { 507 return true; 508 } 509 // 510 AST ast= covering.getAST(); 511 ASTRewrite rewrite= ASTRewrite.create(ast); 512 // create inverted 'if' statement 513 Expression inversedExpression= getInversedExpression(rewrite, ifStatement.getExpression()); 514 IfStatement newIf= ast.newIfStatement(); 515 newIf.setExpression(inversedExpression); 516 newIf.setThenStatement(ast.newContinueStatement()); 517 // 518 if (ifParentBlock == null) { 519 // if there is no block, create it 520 ifParentBlock= ast.newBlock(); 521 ifParentBlock.statements().add(newIf); 522 for (Statement statement : getUnwrappedStatements(ifStatement.getThenStatement())) { 523 ifParentBlock.statements().add(rewrite.createMoveTarget(statement)); 524 } 525 // replace 'if' statement as body with new block 526 if (ifParentStructure instanceof ForStatement) { 527 rewrite.set(ifParentStructure, ForStatement.BODY_PROPERTY, ifParentBlock, null); 528 } else if (ifParentStructure instanceof WhileStatement) { 529 rewrite.set(ifParentStructure, WhileStatement.BODY_PROPERTY, ifParentBlock, null); 530 } 531 } else { 532 // if there was block, replace 533 ListRewrite listRewriter= rewrite.getListRewrite(ifParentBlock, (ChildListPropertyDescriptor) ifStatement.getLocationInParent()); 534 listRewriter.replace(ifStatement, newIf, null); 535 // add statements from 'then' to the end of block 536 for (Statement statement : getUnwrappedStatements(ifStatement.getThenStatement())) { 537 listRewriter.insertLast(rewrite.createMoveTarget(statement), null); 538 } 539 } 540 // add correction proposal 541 String label= CorrectionMessages.AdvancedQuickAssistProcessor_inverseIfToContinue_description; 542 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 543 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.INVERT_IF_TO_CONTINUE, image); 544 resultingCollections.add(proposal); 545 return true; 546 } 547 getUnwrappedStatements(Statement body)548 private static ArrayList<Statement> getUnwrappedStatements(Statement body) { 549 ArrayList<Statement> statements= new ArrayList<>(); 550 if (body instanceof Block) { 551 for (Iterator<Statement> iter= ((Block) body).statements().iterator(); iter.hasNext();) { 552 Statement statement= iter.next(); 553 statements.add(statement); 554 } 555 } else { 556 statements.add(body); 557 } 558 return statements; 559 } 560 getInverseConditionProposals(IInvocationContext context, ASTNode covering, ArrayList<ASTNode> coveredNodes, Collection<ICommandAccess> resultingCollections)561 private static boolean getInverseConditionProposals(IInvocationContext context, ASTNode covering, ArrayList<ASTNode> coveredNodes, Collection<ICommandAccess> resultingCollections) { 562 if (coveredNodes.isEmpty()) { 563 return false; 564 } 565 // 566 final AST ast= covering.getAST(); 567 final ASTRewrite rewrite= ASTRewrite.create(ast); 568 // check sub-expressions in fully covered nodes 569 boolean hasChanges= false; 570 for (ASTNode covered : coveredNodes) { 571 Expression coveredExpression= getBooleanExpression(covered); 572 if (coveredExpression != null) { 573 Expression inversedExpression= getInversedExpression(rewrite, coveredExpression); 574 rewrite.replace(coveredExpression, inversedExpression, null); 575 hasChanges= true; 576 } 577 } 578 // 579 if (!hasChanges) { 580 return false; 581 } 582 if (resultingCollections == null) { 583 return true; 584 } 585 // add correction proposal 586 String label= CorrectionMessages.AdvancedQuickAssistProcessor_inverseConditions_description; 587 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 588 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.INVERSE_CONDITIONS, image); 589 resultingCollections.add(proposal); 590 return true; 591 } 592 getInversedExpression(ASTRewrite rewrite, Expression expression)593 private static Expression getInversedExpression(ASTRewrite rewrite, Expression expression) { 594 return getInversedExpression(rewrite, expression, null); 595 } 596 private interface SimpleNameRenameProvider { getRenamed(SimpleName name)597 SimpleName getRenamed(SimpleName name); 598 } 599 getRenamedNameCopy(SimpleNameRenameProvider provider, ASTRewrite rewrite, Expression expression)600 private static Expression getRenamedNameCopy(SimpleNameRenameProvider provider, ASTRewrite rewrite, Expression expression) { 601 if (provider != null) { 602 if (expression instanceof SimpleName) { 603 SimpleName name= (SimpleName) expression; 604 SimpleName newName= provider.getRenamed(name); 605 if (newName != null) { 606 return newName; 607 } 608 } 609 } 610 return (Expression) rewrite.createCopyTarget(expression); 611 } 612 getInversedExpression(ASTRewrite rewrite, Expression expression, SimpleNameRenameProvider provider)613 private static Expression getInversedExpression(ASTRewrite rewrite, Expression expression, SimpleNameRenameProvider provider) { 614 AST ast= rewrite.getAST(); 615 // 616 if (expression instanceof BooleanLiteral) { 617 return ast.newBooleanLiteral(!((BooleanLiteral) expression).booleanValue()); 618 } 619 if (expression instanceof InfixExpression) { 620 InfixExpression infixExpression= (InfixExpression) expression; 621 InfixExpression.Operator operator= infixExpression.getOperator(); 622 if (operator == InfixExpression.Operator.LESS) { 623 return getInversedInfixExpression(rewrite, infixExpression, InfixExpression.Operator.GREATER_EQUALS, provider); 624 } 625 if (operator == InfixExpression.Operator.GREATER) { 626 return getInversedInfixExpression(rewrite, infixExpression, InfixExpression.Operator.LESS_EQUALS, provider); 627 } 628 if (operator == InfixExpression.Operator.LESS_EQUALS) { 629 return getInversedInfixExpression(rewrite, infixExpression, InfixExpression.Operator.GREATER, provider); 630 } 631 if (operator == InfixExpression.Operator.GREATER_EQUALS) { 632 return getInversedInfixExpression(rewrite, infixExpression, InfixExpression.Operator.LESS, provider); 633 } 634 if (operator == InfixExpression.Operator.EQUALS) { 635 return getInversedInfixExpression(rewrite, infixExpression, InfixExpression.Operator.NOT_EQUALS, provider); 636 } 637 if (operator == InfixExpression.Operator.NOT_EQUALS) { 638 return getInversedInfixExpression(rewrite, infixExpression, InfixExpression.Operator.EQUALS, provider); 639 } 640 if (operator == InfixExpression.Operator.CONDITIONAL_AND) { 641 return getInversedAndOrExpression(rewrite, infixExpression, InfixExpression.Operator.CONDITIONAL_OR, provider); 642 } 643 if (operator == InfixExpression.Operator.CONDITIONAL_OR) { 644 return getInversedAndOrExpression(rewrite, infixExpression, InfixExpression.Operator.CONDITIONAL_AND, provider); 645 } 646 if (operator == InfixExpression.Operator.AND) { 647 return getInversedAndOrExpression(rewrite, infixExpression, InfixExpression.Operator.OR, provider); 648 } 649 if (operator == InfixExpression.Operator.OR) { 650 return getInversedAndOrExpression(rewrite, infixExpression, InfixExpression.Operator.AND, provider); 651 } 652 if (operator == InfixExpression.Operator.XOR) { 653 return getInversedNotExpression(rewrite, expression, ast); 654 } 655 } 656 if (expression instanceof PrefixExpression) { 657 PrefixExpression prefixExpression= (PrefixExpression) expression; 658 if (prefixExpression.getOperator() == PrefixExpression.Operator.NOT) { 659 Expression operand= prefixExpression.getOperand(); 660 if ((operand instanceof ParenthesizedExpression) 661 && NecessaryParenthesesChecker.canRemoveParentheses(operand, expression.getParent(), expression.getLocationInParent())) { 662 operand= ((ParenthesizedExpression)operand).getExpression(); 663 } 664 Expression renamedNameCopy= getRenamedNameCopy(provider, rewrite, operand); 665 if (renamedNameCopy instanceof InfixExpression) { 666 InfixExpression infixExpression= (InfixExpression) renamedNameCopy; 667 infixExpression.setOperator(((InfixExpression) operand).getOperator()); 668 } 669 return renamedNameCopy; 670 } 671 } 672 if (expression instanceof InstanceofExpression) { 673 return getInversedNotExpression(rewrite, expression, ast); 674 } 675 if (expression instanceof ParenthesizedExpression) { 676 ParenthesizedExpression parenthesizedExpression= (ParenthesizedExpression) expression; 677 Expression innerExpression= ASTNodes.getUnparenthesedExpression(parenthesizedExpression.getExpression()); 678 if (innerExpression instanceof InstanceofExpression) { 679 return getInversedExpression(rewrite, innerExpression, provider); 680 } 681 parenthesizedExpression= getParenthesizedExpression(ast, getInversedExpression(rewrite, innerExpression, provider)); 682 return parenthesizedExpression; 683 } 684 if (expression instanceof ConditionalExpression) { 685 ConditionalExpression conditionalExpression= (ConditionalExpression)expression; 686 ConditionalExpression newExpression= ast.newConditionalExpression(); 687 newExpression.setExpression((Expression)rewrite.createCopyTarget(conditionalExpression.getExpression())); 688 newExpression.setThenExpression(getInversedExpression(rewrite, conditionalExpression.getThenExpression())); 689 newExpression.setElseExpression(getInversedExpression(rewrite, conditionalExpression.getElseExpression())); 690 return newExpression; 691 } 692 693 PrefixExpression prefixExpression= ast.newPrefixExpression(); 694 prefixExpression.setOperator(PrefixExpression.Operator.NOT); 695 Expression renamedNameCopy= getRenamedNameCopy(provider, rewrite, expression); 696 if (NecessaryParenthesesChecker.needsParentheses(renamedNameCopy, prefixExpression, PrefixExpression.OPERAND_PROPERTY)) { 697 renamedNameCopy= getParenthesizedExpression(ast, renamedNameCopy); 698 } 699 prefixExpression.setOperand(renamedNameCopy); 700 return prefixExpression; 701 } 702 getInversedNotExpression(ASTRewrite rewrite, Expression expression, AST ast)703 private static Expression getInversedNotExpression(ASTRewrite rewrite, Expression expression, AST ast) { 704 PrefixExpression prefixExpression= ast.newPrefixExpression(); 705 prefixExpression.setOperator(PrefixExpression.Operator.NOT); 706 ParenthesizedExpression parenthesizedExpression= getParenthesizedExpression(ast, (Expression)rewrite.createCopyTarget(expression)); 707 prefixExpression.setOperand(parenthesizedExpression); 708 return prefixExpression; 709 } 710 isBoolean(Expression expression)711 private static boolean isBoolean(Expression expression) { 712 ITypeBinding typeBinding= expression.resolveTypeBinding(); 713 AST ast= expression.getAST(); 714 return typeBinding == ast.resolveWellKnownType("boolean") //$NON-NLS-1$ 715 || typeBinding == ast.resolveWellKnownType("java.lang.Boolean"); //$NON-NLS-1$ 716 } 717 getInversedInfixExpression(ASTRewrite rewrite, InfixExpression expression, InfixExpression.Operator newOperator, SimpleNameRenameProvider provider)718 private static Expression getInversedInfixExpression(ASTRewrite rewrite, InfixExpression expression, InfixExpression.Operator newOperator, SimpleNameRenameProvider provider) { 719 InfixExpression newExpression= rewrite.getAST().newInfixExpression(); 720 newExpression.setOperator(newOperator); 721 newExpression.setLeftOperand(getRenamedNameCopy(provider, rewrite, expression.getLeftOperand())); 722 newExpression.setRightOperand(getRenamedNameCopy(provider, rewrite, expression.getRightOperand())); 723 return newExpression; 724 } 725 parenthesizeIfRequired(Expression operand, int newOperatorPrecedence)726 private static Expression parenthesizeIfRequired(Expression operand, int newOperatorPrecedence) { 727 if (newOperatorPrecedence > OperatorPrecedence.getExpressionPrecedence(operand)) { 728 return getParenthesizedExpression(operand.getAST(), operand); 729 } 730 return operand; 731 } 732 getInversedAndOrExpression(ASTRewrite rewrite, InfixExpression infixExpression, Operator newOperator, SimpleNameRenameProvider provider)733 private static Expression getInversedAndOrExpression(ASTRewrite rewrite, InfixExpression infixExpression, Operator newOperator, SimpleNameRenameProvider provider) { 734 InfixExpression newExpression= rewrite.getAST().newInfixExpression(); 735 newExpression.setOperator(newOperator); 736 737 int newOperatorPrecedence= OperatorPrecedence.getOperatorPrecedence(newOperator); 738 // 739 Expression leftOperand= getInversedExpression(rewrite, infixExpression.getLeftOperand(), provider); 740 newExpression.setLeftOperand(parenthesizeIfRequired(leftOperand, newOperatorPrecedence)); 741 742 Expression rightOperand= getInversedExpression(rewrite, infixExpression.getRightOperand(), provider); 743 newExpression.setRightOperand(parenthesizeIfRequired(rightOperand, newOperatorPrecedence)); 744 745 List<Expression> extraOperands= infixExpression.extendedOperands(); 746 List<Expression> newExtraOperands= newExpression.extendedOperands(); 747 for (int i= 0; i < extraOperands.size(); i++) { 748 Expression extraOperand= getInversedExpression(rewrite, extraOperands.get(i), provider); 749 newExtraOperands.add(parenthesizeIfRequired(extraOperand, newOperatorPrecedence)); 750 } 751 return newExpression; 752 } 753 getRemoveExtraParenthesesProposals(IInvocationContext context, ASTNode covering, ArrayList<ASTNode> coveredNodes, Collection<ICommandAccess> resultingCollections)754 private static boolean getRemoveExtraParenthesesProposals(IInvocationContext context, ASTNode covering, ArrayList<ASTNode> coveredNodes, Collection<ICommandAccess> resultingCollections) { 755 ArrayList<ASTNode> nodes; 756 if (context.getSelectionLength() == 0 && covering instanceof ParenthesizedExpression) { 757 nodes= new ArrayList<>(); 758 nodes.add(covering); 759 } else { 760 nodes= coveredNodes; 761 } 762 if (nodes.isEmpty()) 763 return false; 764 765 IProposableFix fix= ExpressionsFix.createRemoveUnnecessaryParenthesisFix(context.getASTRoot(), nodes.toArray(new ASTNode[nodes.size()])); 766 if (fix == null) 767 return false; 768 769 if (resultingCollections == null) 770 return true; 771 772 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_REMOVE); 773 Map<String, String> options= new Hashtable<>(); 774 options.put(CleanUpConstants.EXPRESSIONS_USE_PARENTHESES, CleanUpOptions.TRUE); 775 options.put(CleanUpConstants.EXPRESSIONS_USE_PARENTHESES_NEVER, CleanUpOptions.TRUE); 776 FixCorrectionProposal proposal= new FixCorrectionProposal(fix, new ExpressionsCleanUp(options), IProposalRelevance.REMOVE_EXTRA_PARENTHESES, image, context); 777 resultingCollections.add(proposal); 778 return true; 779 } 780 getAddParanoidalParenthesesProposals(IInvocationContext context, ArrayList<ASTNode> coveredNodes, Collection<ICommandAccess> resultingCollections)781 private static boolean getAddParanoidalParenthesesProposals(IInvocationContext context, ArrayList<ASTNode> coveredNodes, Collection<ICommandAccess> resultingCollections) { 782 783 IProposableFix fix= ExpressionsFix.createAddParanoidalParenthesisFix(context.getASTRoot(), coveredNodes.toArray(new ASTNode[coveredNodes.size()])); 784 if (fix == null) 785 return false; 786 787 if (resultingCollections == null) 788 return true; 789 790 // add correction proposal 791 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CAST); 792 Map<String, String> options= new Hashtable<>(); 793 options.put(CleanUpConstants.EXPRESSIONS_USE_PARENTHESES, CleanUpOptions.TRUE); 794 options.put(CleanUpConstants.EXPRESSIONS_USE_PARENTHESES_ALWAYS, CleanUpOptions.TRUE); 795 FixCorrectionProposal proposal= new FixCorrectionProposal(fix, new ExpressionsCleanUp(options), IProposalRelevance.ADD_PARANOIDAL_PARENTHESES, image, context); 796 resultingCollections.add(proposal); 797 return true; 798 } 799 getAddParenthesesForExpressionProposals(IInvocationContext context, ASTNode coveringNode, Collection<ICommandAccess> resultingCollections)800 private static boolean getAddParenthesesForExpressionProposals(IInvocationContext context, ASTNode coveringNode, Collection<ICommandAccess> resultingCollections) { 801 ASTNode node; 802 803 if (context.getSelectionLength() == 0) { 804 node= coveringNode; 805 while (node != null && !(node instanceof CastExpression) && !(node instanceof InfixExpression) && !(node instanceof InstanceofExpression) && !(node instanceof ConditionalExpression)) { 806 node= node.getParent(); 807 } 808 } else { 809 node= context.getCoveredNode(); 810 } 811 812 String label= null; 813 if (node instanceof CastExpression) { 814 label= CorrectionMessages.UnresolvedElementsSubProcessor_missingcastbrackets_description; 815 } else if (node instanceof InstanceofExpression) { 816 label= CorrectionMessages.LocalCorrectionsSubProcessor_setparenteses_instanceof_description; 817 } else if (node instanceof InfixExpression) { 818 InfixExpression infixExpression= (InfixExpression)node; 819 label= Messages.format(CorrectionMessages.LocalCorrectionsSubProcessor_setparenteses_description, infixExpression.getOperator().toString()); 820 } else if (node instanceof ConditionalExpression) { 821 label= CorrectionMessages.AdvancedQuickAssistProcessor_putConditionalExpressionInParentheses; 822 } else { 823 return false; 824 } 825 826 if (node.getParent() instanceof ParenthesizedExpression) 827 return false; 828 829 if (resultingCollections == null) 830 return true; 831 832 AST ast= node.getAST(); 833 ASTRewrite rewrite= ASTRewrite.create(ast); 834 835 ParenthesizedExpression parenthesizedExpression= ast.newParenthesizedExpression(); 836 parenthesizedExpression.setExpression((Expression)rewrite.createCopyTarget(node)); 837 rewrite.replace(node, parenthesizedExpression, null); 838 839 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CAST); 840 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.ADD_PARENTHESES_FOR_EXPRESSION, image); 841 resultingCollections.add(proposal); 842 return true; 843 } 844 getFullyCoveredNodes(IInvocationContext context, ASTNode coveringNode)845 static ArrayList<ASTNode> getFullyCoveredNodes(IInvocationContext context, ASTNode coveringNode) { 846 final ArrayList<ASTNode> coveredNodes= new ArrayList<>(); 847 final int selectionBegin= context.getSelectionOffset(); 848 final int selectionEnd= selectionBegin + context.getSelectionLength(); 849 coveringNode.accept(new GenericVisitor() { 850 @Override 851 protected boolean visitNode(ASTNode node) { 852 int nodeStart= node.getStartPosition(); 853 int nodeEnd= nodeStart + node.getLength(); 854 // if node does not intersects with selection, don't visit children 855 if (nodeEnd < selectionBegin || selectionEnd < nodeStart) { 856 return false; 857 } 858 // if node is fully covered, we don't need to visit children 859 if (isCovered(node)) { 860 ASTNode parent= node.getParent(); 861 if (parent == null || !isCovered(parent)) { 862 coveredNodes.add(node); 863 return false; 864 } 865 } 866 // if node only partly intersects with selection, we try to find fully covered children 867 return true; 868 } 869 870 private boolean isCovered(ASTNode node) { 871 int begin= node.getStartPosition(); 872 int end= begin + node.getLength(); 873 return begin >= selectionBegin && end <= selectionEnd; 874 } 875 }); 876 return coveredNodes; 877 } 878 getJoinAndIfStatementsProposals(IInvocationContext context, ASTNode node, Collection<ICommandAccess> resultingCollections)879 private static boolean getJoinAndIfStatementsProposals(IInvocationContext context, ASTNode node, Collection<ICommandAccess> resultingCollections) { 880 881 // 882 if (!(node instanceof IfStatement)) { 883 return false; 884 } 885 IfStatement ifStatement= (IfStatement) node; 886 if (ifStatement.getElseStatement() != null) { 887 return false; 888 } 889 // case when current IfStatement is sole child of another IfStatement 890 { 891 IfStatement outerIf= null; 892 if (ifStatement.getParent() instanceof IfStatement) { 893 outerIf= (IfStatement) ifStatement.getParent(); 894 } else if (ifStatement.getParent() instanceof Block) { 895 Block block= (Block) ifStatement.getParent(); 896 if (block.getParent() instanceof IfStatement && block.statements().size() == 1) { 897 outerIf= (IfStatement) block.getParent(); 898 } 899 } 900 if (outerIf != null && outerIf.getElseStatement() == null) { 901 if (resultingCollections == null) { 902 return true; 903 } 904 // 905 AST ast= node.getAST(); 906 ASTRewrite rewrite= ASTRewrite.create(ast); 907 // create compound condition 908 InfixExpression condition= ast.newInfixExpression(); 909 condition.setOperator(InfixExpression.Operator.CONDITIONAL_AND); 910 // prepare condition parts, add parentheses if needed 911 Expression outerCondition= getParenthesizedExpressionIfNeeded(ast, rewrite, outerIf.getExpression(), condition, InfixExpression.LEFT_OPERAND_PROPERTY); 912 Expression innerCondition= getParenthesizedExpressionIfNeeded(ast, rewrite, ifStatement.getExpression(), condition, InfixExpression.RIGHT_OPERAND_PROPERTY); 913 condition.setLeftOperand(outerCondition); 914 condition.setRightOperand(innerCondition); 915 // create new IfStatement 916 IfStatement newIf= ast.newIfStatement(); 917 newIf.setExpression(condition); 918 Statement bodyPlaceholder= (Statement) rewrite.createCopyTarget(ifStatement.getThenStatement()); 919 newIf.setThenStatement(bodyPlaceholder); 920 rewrite.setTargetSourceRangeComputer(new NoCommentSourceRangeComputer()); 921 rewrite.replace(outerIf, newIf, null); 922 // add correction proposal 923 String label= CorrectionMessages.AdvancedQuickAssistProcessor_joinWithOuter_description; 924 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 925 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.JOIN_IF_WITH_OUTER_IF, image); 926 resultingCollections.add(proposal); 927 } 928 } 929 // case when current IfStatement has another IfStatement as sole child 930 { 931 IfStatement innerIf= null; 932 if (ifStatement.getThenStatement() instanceof IfStatement) { 933 innerIf= (IfStatement) ifStatement.getThenStatement(); 934 } else if (ifStatement.getThenStatement() instanceof Block) { 935 Block block= (Block) ifStatement.getThenStatement(); 936 if (block.statements().size() == 1 && block.statements().get(0) instanceof IfStatement) { 937 innerIf= (IfStatement) block.statements().get(0); 938 } 939 } 940 if (innerIf != null && innerIf.getElseStatement() == null) { 941 if (resultingCollections == null) { 942 return true; 943 } 944 // 945 AST ast= node.getAST(); 946 ASTRewrite rewrite= ASTRewrite.create(ast); 947 // create compound condition 948 InfixExpression condition= ast.newInfixExpression(); 949 condition.setOperator(InfixExpression.Operator.CONDITIONAL_AND); 950 // prepare condition parts, add parentheses if needed 951 Expression outerCondition= getParenthesizedExpressionIfNeeded(ast, rewrite, ifStatement.getExpression(), condition, InfixExpression.LEFT_OPERAND_PROPERTY); 952 Expression innerCondition= getParenthesizedExpressionIfNeeded(ast, rewrite, innerIf.getExpression(), condition, InfixExpression.RIGHT_OPERAND_PROPERTY); 953 condition.setLeftOperand(outerCondition); 954 condition.setRightOperand(innerCondition); 955 // create new IfStatement 956 IfStatement newIf= ast.newIfStatement(); 957 newIf.setExpression(condition); 958 Statement bodyPlaceholder= (Statement) rewrite.createCopyTarget(innerIf.getThenStatement()); 959 newIf.setThenStatement(bodyPlaceholder); 960 rewrite.setTargetSourceRangeComputer(new NoCommentSourceRangeComputer()); 961 rewrite.replace(ifStatement, newIf, null); 962 // add correction proposal 963 String label= CorrectionMessages.AdvancedQuickAssistProcessor_joinWithInner_description; 964 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 965 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.JOIN_IF_WITH_INNER_IF, image); 966 resultingCollections.add(proposal); 967 } 968 } 969 return true; 970 } 971 getParenthesizedExpressionIfNeeded(AST ast, ASTRewrite rewrite, Expression expression, ASTNode parent, StructuralPropertyDescriptor locationInParent)972 private static Expression getParenthesizedExpressionIfNeeded(AST ast, ASTRewrite rewrite, Expression expression, ASTNode parent, StructuralPropertyDescriptor locationInParent) { 973 boolean addParentheses= NecessaryParenthesesChecker.needsParentheses(expression, parent, locationInParent); 974 expression= (Expression)rewrite.createCopyTarget(expression); 975 if (addParentheses) { 976 return getParenthesizedExpression(ast, expression); 977 } 978 return expression; 979 } 980 getParenthesizedExpression(AST ast, Expression expression)981 private static ParenthesizedExpression getParenthesizedExpression(AST ast, Expression expression) { 982 ParenthesizedExpression parenthesizedExpression= ast.newParenthesizedExpression(); 983 parenthesizedExpression.setExpression(expression); 984 return parenthesizedExpression; 985 } 986 getSplitAndConditionProposals(IInvocationContext context, ASTNode node, Collection<ICommandAccess> resultingCollections)987 public static boolean getSplitAndConditionProposals(IInvocationContext context, ASTNode node, Collection<ICommandAccess> resultingCollections) { 988 Operator andOperator= InfixExpression.Operator.CONDITIONAL_AND; 989 // check that user invokes quick assist on infix expression 990 if (!(node instanceof InfixExpression)) { 991 return false; 992 } 993 InfixExpression infixExpression= (InfixExpression) node; 994 if (infixExpression.getOperator() != andOperator) { 995 return false; 996 } 997 int offset= isOperatorSelected(infixExpression, context.getSelectionOffset(), context.getSelectionLength()); 998 if (offset == -1) { 999 return false; 1000 } 1001 1002 // check that infix expression belongs to IfStatement 1003 Statement statement= ASTResolving.findParentStatement(node); 1004 if (!(statement instanceof IfStatement)) { 1005 return false; 1006 } 1007 IfStatement ifStatement= (IfStatement) statement; 1008 1009 // check that infix expression is part of first level && condition of IfStatement 1010 InfixExpression topInfixExpression= infixExpression; 1011 while (topInfixExpression.getParent() instanceof InfixExpression && ((InfixExpression) topInfixExpression.getParent()).getOperator() == andOperator) { 1012 topInfixExpression= (InfixExpression) topInfixExpression.getParent(); 1013 } 1014 if (ifStatement.getExpression() != topInfixExpression) { 1015 return false; 1016 } 1017 // 1018 if (resultingCollections == null) { 1019 return true; 1020 } 1021 AST ast= ifStatement.getAST(); 1022 ASTRewrite rewrite= ASTRewrite.create(ast); 1023 1024 // prepare left and right conditions 1025 Expression[] newOperands= { null, null }; 1026 breakInfixOperationAtOperation(rewrite, topInfixExpression, andOperator, offset, true, newOperands); 1027 1028 Expression leftCondition= newOperands[0]; 1029 Expression rightCondition= newOperands[1]; 1030 1031 // replace conditions in outer IfStatement 1032 rewrite.set(ifStatement, IfStatement.EXPRESSION_PROPERTY, leftCondition, null); 1033 1034 // prepare inner IfStatement 1035 IfStatement innerIf= ast.newIfStatement(); 1036 1037 innerIf.setExpression(rightCondition); 1038 innerIf.setThenStatement((Statement) rewrite.createMoveTarget(ifStatement.getThenStatement())); 1039 Block innerBlock= ast.newBlock(); 1040 innerBlock.statements().add(innerIf); 1041 1042 Statement elseStatement= ifStatement.getElseStatement(); 1043 if (elseStatement != null) { 1044 innerIf.setElseStatement((Statement) rewrite.createCopyTarget(elseStatement)); 1045 } 1046 1047 // replace outer thenStatement 1048 rewrite.replace(ifStatement.getThenStatement(), innerBlock, null); 1049 1050 // add correction proposal 1051 String label= CorrectionMessages.AdvancedQuickAssistProcessor_splitAndCondition_description; 1052 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 1053 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.SPLIT_AND_CONDITION, image); 1054 resultingCollections.add(proposal); 1055 return true; 1056 } 1057 isSelectingOperator(ASTNode n1, ASTNode n2, int offset, int length)1058 private static boolean isSelectingOperator(ASTNode n1, ASTNode n2, int offset, int length) { 1059 // between the nodes 1060 if (offset + length <= n2.getStartPosition() && offset >= ASTNodes.getExclusiveEnd(n1)) { 1061 return true; 1062 } 1063 // or exactly select the node (but not with infix expressions) 1064 if (n1.getStartPosition() == offset && ASTNodes.getExclusiveEnd(n2) == offset + length) { 1065 if (n1 instanceof InfixExpression || n2 instanceof InfixExpression) { 1066 return false; 1067 } 1068 return true; 1069 } 1070 return false; 1071 } 1072 isOperatorSelected(InfixExpression infixExpression, int offset, int length)1073 private static int isOperatorSelected(InfixExpression infixExpression, int offset, int length) { 1074 ASTNode left= infixExpression.getLeftOperand(); 1075 ASTNode right= infixExpression.getRightOperand(); 1076 1077 if (isSelectingOperator(left, right, offset, length)) { 1078 return ASTNodes.getExclusiveEnd(left); 1079 } 1080 List<Expression> extended= infixExpression.extendedOperands(); 1081 for (Expression element : extended) { 1082 left= right; 1083 right= element; 1084 if (isSelectingOperator(left, right, offset, length)) { 1085 return ASTNodes.getExclusiveEnd(left); 1086 } 1087 } 1088 return -1; 1089 } 1090 getJoinOrIfStatementsProposals(IInvocationContext context, ASTNode covering, ArrayList<ASTNode> coveredNodes, Collection<ICommandAccess> resultingCollections)1091 private static boolean getJoinOrIfStatementsProposals(IInvocationContext context, ASTNode covering, ArrayList<ASTNode> coveredNodes, Collection<ICommandAccess> resultingCollections) { 1092 Operator orOperator= InfixExpression.Operator.CONDITIONAL_OR; 1093 if (coveredNodes.size() < 2) 1094 return false; 1095 // check that all covered nodes are IfStatement's with same 'then' statement and without 'else' 1096 String commonThenSource= null; 1097 for (ASTNode node : coveredNodes) { 1098 if (!(node instanceof IfStatement)) 1099 return false; 1100 // 1101 IfStatement ifStatement= (IfStatement) node; 1102 if (ifStatement.getElseStatement() != null) 1103 return false; 1104 // 1105 Statement thenStatement= ifStatement.getThenStatement(); 1106 try { 1107 String thenSource= context.getCompilationUnit().getBuffer().getText(thenStatement.getStartPosition(), thenStatement.getLength()); 1108 if (commonThenSource == null) { 1109 commonThenSource= thenSource; 1110 } else { 1111 if (!commonThenSource.equals(thenSource)) 1112 return false; 1113 } 1114 } catch (Throwable e) { 1115 return false; 1116 } 1117 } 1118 if (resultingCollections == null) { 1119 return true; 1120 } 1121 // 1122 final AST ast= covering.getAST(); 1123 final ASTRewrite rewrite= ASTRewrite.create(ast); 1124 // prepare OR'ed condition 1125 InfixExpression condition= null; 1126 boolean hasRightOperand= false; 1127 Statement thenStatement= null; 1128 for (ASTNode astNode : coveredNodes) { 1129 IfStatement ifStatement= (IfStatement) astNode; 1130 if (thenStatement == null) 1131 thenStatement= (Statement) rewrite.createCopyTarget(ifStatement.getThenStatement()); 1132 if (condition == null) { 1133 condition= ast.newInfixExpression(); 1134 condition.setOperator(orOperator); 1135 condition.setLeftOperand(getParenthesizedExpressionIfNeeded(ast, rewrite, ifStatement.getExpression(), condition, InfixExpression.LEFT_OPERAND_PROPERTY)); 1136 } else if (!hasRightOperand) { 1137 condition.setRightOperand(getParenthesizedExpressionIfNeeded(ast, rewrite, ifStatement.getExpression(), condition, InfixExpression.RIGHT_OPERAND_PROPERTY)); 1138 hasRightOperand= true; 1139 } else { 1140 InfixExpression newCondition= ast.newInfixExpression(); 1141 newCondition.setOperator(orOperator); 1142 newCondition.setLeftOperand(condition); 1143 newCondition.setRightOperand(getParenthesizedExpressionIfNeeded(ast, rewrite, ifStatement.getExpression(), condition, InfixExpression.RIGHT_OPERAND_PROPERTY)); 1144 condition= newCondition; 1145 } 1146 } 1147 // prepare new IfStatement with OR'ed condition 1148 IfStatement newIf= ast.newIfStatement(); 1149 newIf.setExpression(condition); 1150 newIf.setThenStatement(thenStatement); 1151 // 1152 ListRewrite listRewriter= null; 1153 for (ASTNode astNode : coveredNodes) { 1154 IfStatement ifStatement= (IfStatement) astNode; 1155 if (listRewriter == null) { 1156 Block sourceBlock= (Block) ifStatement.getParent(); 1157 //int insertIndex = sourceBlock.statements().indexOf(ifStatement); 1158 listRewriter= rewrite.getListRewrite(sourceBlock, (ChildListPropertyDescriptor) ifStatement.getLocationInParent()); 1159 } 1160 if (newIf != null) { 1161 listRewriter.replace(ifStatement, newIf, null); 1162 newIf= null; 1163 } else { 1164 listRewriter.remove(ifStatement, null); 1165 } 1166 } 1167 // add correction proposal 1168 String label= CorrectionMessages.AdvancedQuickAssistProcessor_joinWithOr_description; 1169 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 1170 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.JOIN_IF_STATEMENTS_WITH_OR, image); 1171 resultingCollections.add(proposal); 1172 return true; 1173 } 1174 getSplitOrConditionProposals(IInvocationContext context, ASTNode node, Collection<ICommandAccess> resultingCollections)1175 public static boolean getSplitOrConditionProposals(IInvocationContext context, ASTNode node, Collection<ICommandAccess> resultingCollections) { 1176 Operator orOperator= InfixExpression.Operator.CONDITIONAL_OR; 1177 // check that user invokes quick assist on infix expression 1178 if (!(node instanceof InfixExpression)) { 1179 return false; 1180 } 1181 InfixExpression infixExpression= (InfixExpression) node; 1182 if (infixExpression.getOperator() != orOperator) { 1183 return false; 1184 } 1185 int offset= isOperatorSelected(infixExpression, context.getSelectionOffset(), context.getSelectionLength()); 1186 if (offset == -1) { 1187 return false; 1188 } 1189 // check that infix expression belongs to IfStatement 1190 Statement statement= ASTResolving.findParentStatement(node); 1191 if (!(statement instanceof IfStatement)) { 1192 return false; 1193 } 1194 IfStatement ifStatement= (IfStatement) statement; 1195 1196 // check that infix expression is part of first level || condition of IfStatement 1197 InfixExpression topInfixExpression= infixExpression; 1198 while (topInfixExpression.getParent() instanceof InfixExpression && ((InfixExpression) topInfixExpression.getParent()).getOperator() == orOperator) { 1199 topInfixExpression= (InfixExpression) topInfixExpression.getParent(); 1200 } 1201 if (ifStatement.getExpression() != topInfixExpression) { 1202 return false; 1203 } 1204 // 1205 if (resultingCollections == null) { 1206 return true; 1207 } 1208 AST ast= ifStatement.getAST(); 1209 ASTRewrite rewrite= ASTRewrite.create(ast); 1210 1211 // prepare left and right conditions 1212 Expression[] newOperands= { null, null }; 1213 breakInfixOperationAtOperation(rewrite, topInfixExpression, orOperator, offset, true, newOperands); 1214 1215 Expression leftCondition= newOperands[0]; 1216 Expression rightCondition= newOperands[1]; 1217 1218 // prepare first statement 1219 rewrite.replace(ifStatement.getExpression(), leftCondition, null); 1220 1221 IfStatement secondIf= ast.newIfStatement(); 1222 secondIf.setExpression(rightCondition); 1223 secondIf.setThenStatement((Statement) rewrite.createCopyTarget(ifStatement.getThenStatement())); 1224 1225 Statement elseStatement= ifStatement.getElseStatement(); 1226 if (elseStatement == null) { 1227 rewrite.set(ifStatement, IfStatement.ELSE_STATEMENT_PROPERTY, secondIf, null); 1228 } else { 1229 rewrite.replace(elseStatement, secondIf, null); 1230 secondIf.setElseStatement((Statement) rewrite.createMoveTarget(elseStatement)); 1231 } 1232 1233 // add correction proposal 1234 String label= CorrectionMessages.AdvancedQuickAssistProcessor_splitOrCondition_description; 1235 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 1236 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.SPLIT_OR_CONDITION, image); 1237 resultingCollections.add(proposal); 1238 return true; 1239 } 1240 getInverseConditionalExpressionProposals(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections)1241 private static boolean getInverseConditionalExpressionProposals(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections) { 1242 // try to find conditional expression as parent 1243 while (covering instanceof Expression) { 1244 if (covering instanceof ConditionalExpression) 1245 break; 1246 covering= covering.getParent(); 1247 } 1248 if (!(covering instanceof ConditionalExpression)) { 1249 return false; 1250 } 1251 ConditionalExpression expression= (ConditionalExpression) covering; 1252 // we could produce quick assist 1253 if (resultingCollections == null) { 1254 return true; 1255 } 1256 // 1257 AST ast= covering.getAST(); 1258 ASTRewrite rewrite= ASTRewrite.create(ast); 1259 // prepare new conditional expression 1260 ConditionalExpression newExpression= ast.newConditionalExpression(); 1261 newExpression.setExpression(getInversedExpression(rewrite, expression.getExpression())); 1262 newExpression.setThenExpression((Expression) rewrite.createCopyTarget(expression.getElseExpression())); 1263 newExpression.setElseExpression((Expression) rewrite.createCopyTarget(expression.getThenExpression())); 1264 // replace old expression with new 1265 rewrite.replace(expression, newExpression, null); 1266 // add correction proposal 1267 String label= CorrectionMessages.AdvancedQuickAssistProcessor_inverseConditionalExpression_description; 1268 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 1269 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.INVERSE_CONDITIONAL_EXPRESSION, image); 1270 resultingCollections.add(proposal); 1271 return true; 1272 } 1273 getExchangeInnerAndOuterIfConditionsProposals(IInvocationContext context, ASTNode node, Collection<ICommandAccess> resultingCollections)1274 private static boolean getExchangeInnerAndOuterIfConditionsProposals(IInvocationContext context, ASTNode node, Collection<ICommandAccess> resultingCollections) { 1275 boolean result= false; 1276 // 1277 if (!(node instanceof IfStatement)) { 1278 return false; 1279 } 1280 IfStatement ifStatement= (IfStatement) node; 1281 if (ifStatement.getElseStatement() != null) { 1282 return false; 1283 } 1284 // case when current IfStatement is sole child of another IfStatement 1285 { 1286 IfStatement outerIf= null; 1287 if (ifStatement.getParent() instanceof IfStatement) { 1288 outerIf= (IfStatement) ifStatement.getParent(); 1289 } else if (ifStatement.getParent() instanceof Block) { 1290 Block block= (Block) ifStatement.getParent(); 1291 if (block.getParent() instanceof IfStatement && block.statements().size() == 1) { 1292 outerIf= (IfStatement) block.getParent(); 1293 } 1294 } 1295 if (outerIf != null && outerIf.getElseStatement() == null) { 1296 if (resultingCollections == null) { 1297 return true; 1298 } 1299 // 1300 AST ast= node.getAST(); 1301 ASTRewrite rewrite= ASTRewrite.create(ast); 1302 // prepare conditions 1303 Expression outerCondition= (Expression) rewrite.createCopyTarget(outerIf.getExpression()); 1304 Expression innerCondition= (Expression) rewrite.createCopyTarget(ifStatement.getExpression()); 1305 // exchange conditions 1306 rewrite.replace(outerIf.getExpression(), innerCondition, null); 1307 rewrite.replace(ifStatement.getExpression(), outerCondition, null); 1308 // add correction proposal 1309 String label= CorrectionMessages.AdvancedQuickAssistProcessor_exchangeInnerAndOuterIfConditions_description; 1310 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 1311 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.EXCHANGE_INNER_AND_OUTER_IF_CONDITIONS, image); 1312 resultingCollections.add(proposal); 1313 result= true; 1314 } 1315 } 1316 // case when current IfStatement has another IfStatement as sole child 1317 { 1318 IfStatement innerIf= null; 1319 if (ifStatement.getThenStatement() instanceof IfStatement) { 1320 innerIf= (IfStatement) ifStatement.getThenStatement(); 1321 } else if (ifStatement.getThenStatement() instanceof Block) { 1322 Block block= (Block) ifStatement.getThenStatement(); 1323 if (block.statements().size() == 1 && block.statements().get(0) instanceof IfStatement) { 1324 innerIf= (IfStatement) block.statements().get(0); 1325 } 1326 } 1327 if (innerIf != null && innerIf.getElseStatement() == null) { 1328 if (resultingCollections == null) { 1329 return true; 1330 } 1331 // 1332 AST ast= node.getAST(); 1333 ASTRewrite rewrite= ASTRewrite.create(ast); 1334 // prepare conditions 1335 Expression innerCondition= (Expression) rewrite.createCopyTarget(innerIf.getExpression()); 1336 Expression outerCondition= (Expression) rewrite.createCopyTarget(ifStatement.getExpression()); 1337 // exchange conditions 1338 rewrite.replace(innerIf.getExpression(), outerCondition, null); 1339 rewrite.replace(ifStatement.getExpression(), innerCondition, null); 1340 // add correction proposal 1341 String label= CorrectionMessages.AdvancedQuickAssistProcessor_exchangeInnerAndOuterIfConditions_description; 1342 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 1343 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.EXCHANGE_INNER_AND_OUTER_IF_CONDITIONS, image); 1344 resultingCollections.add(proposal); 1345 result= true; 1346 } 1347 } 1348 return result; 1349 } 1350 getExchangeOperandsProposals(IInvocationContext context, ASTNode node, Collection<ICommandAccess> resultingCollections)1351 private static boolean getExchangeOperandsProposals(IInvocationContext context, ASTNode node, Collection<ICommandAccess> resultingCollections) { 1352 // check that user invokes quick assist on infix expression 1353 if (!(node instanceof InfixExpression)) { 1354 return false; 1355 } 1356 InfixExpression infixExpression= (InfixExpression)node; 1357 Operator operator= infixExpression.getOperator(); 1358 if (operator != InfixExpression.Operator.CONDITIONAL_AND && operator != InfixExpression.Operator.AND 1359 && operator != InfixExpression.Operator.CONDITIONAL_OR && operator != InfixExpression.Operator.OR 1360 && operator != InfixExpression.Operator.EQUALS && operator != InfixExpression.Operator.NOT_EQUALS 1361 && operator != InfixExpression.Operator.LESS && operator != InfixExpression.Operator.LESS_EQUALS 1362 && operator != InfixExpression.Operator.GREATER && operator != InfixExpression.Operator.GREATER_EQUALS 1363 && operator != InfixExpression.Operator.PLUS && operator != InfixExpression.Operator.TIMES 1364 && operator != InfixExpression.Operator.XOR) { 1365 return false; 1366 } 1367 1368 int offset= isOperatorSelected(infixExpression, context.getSelectionOffset(), context.getSelectionLength()); 1369 if (offset == -1) { 1370 return false; 1371 } 1372 1373 // we could produce quick assist 1374 if (resultingCollections == null) { 1375 return true; 1376 } 1377 AST ast= infixExpression.getAST(); 1378 ASTRewrite rewrite= ASTRewrite.create(ast); 1379 // prepare left and right expressions 1380 Expression leftExpression= null; 1381 Expression rightExpression= null; 1382 InfixExpression currentExpression= infixExpression; 1383 leftExpression= combineOperands(rewrite, leftExpression, infixExpression.getLeftOperand(), false, operator); 1384 if (infixExpression.getRightOperand().getStartPosition() <= context.getSelectionOffset()) { 1385 leftExpression= combineOperands(rewrite, leftExpression, infixExpression.getRightOperand(), false, operator); 1386 } else { 1387 rightExpression= combineOperands(rewrite, rightExpression, infixExpression.getRightOperand(), false, operator); 1388 } 1389 for (Iterator<Expression> iter= currentExpression.extendedOperands().iterator(); iter.hasNext();) { 1390 Expression extendedOperand= iter.next(); 1391 if (extendedOperand.getStartPosition() <= context.getSelectionOffset()) { 1392 leftExpression= combineOperands(rewrite, leftExpression, extendedOperand, false, operator); 1393 } else { 1394 rightExpression= combineOperands(rewrite, rightExpression, extendedOperand, false, operator); 1395 } 1396 } 1397 1398 if (NecessaryParenthesesChecker.needsParentheses(leftExpression, infixExpression, InfixExpression.RIGHT_OPERAND_PROPERTY)) { 1399 leftExpression= getParenthesizedExpression(ast, leftExpression); 1400 } 1401 if (NecessaryParenthesesChecker.needsParentheses(rightExpression, infixExpression, InfixExpression.LEFT_OPERAND_PROPERTY)) { 1402 rightExpression= getParenthesizedExpression(ast, rightExpression); 1403 } 1404 1405 if (operator == InfixExpression.Operator.LESS) { 1406 operator= InfixExpression.Operator.GREATER; 1407 } else if (operator == InfixExpression.Operator.LESS_EQUALS) { 1408 operator= InfixExpression.Operator.GREATER_EQUALS; 1409 } else if (operator == InfixExpression.Operator.GREATER) { 1410 operator= InfixExpression.Operator.LESS; 1411 } else if (operator == InfixExpression.Operator.GREATER_EQUALS) { 1412 operator= InfixExpression.Operator.LESS_EQUALS; 1413 } 1414 1415 // create new infix expression 1416 InfixExpression newInfix= ast.newInfixExpression(); 1417 newInfix.setOperator(operator); 1418 newInfix.setLeftOperand(rightExpression); 1419 newInfix.setRightOperand(leftExpression); 1420 rewrite.replace(infixExpression, newInfix, null); 1421 // add correction proposal 1422 String label= CorrectionMessages.AdvancedQuickAssistProcessor_exchangeOperands_description; 1423 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 1424 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.EXCHANGE_OPERANDS, image); 1425 resultingCollections.add(proposal); 1426 return true; 1427 } 1428 1429 /* 1430 * Breaks an infix operation with possible extended operators at the given operator and returns the new left and right operands. 1431 * a & b & c -> [[a' & b' ] & c' ] (c' == copy of c) 1432 */ breakInfixOperationAtOperation(ASTRewrite rewrite, Expression expression, Operator operator, int operatorOffset, boolean removeParentheses, Expression[] res)1433 private static void breakInfixOperationAtOperation(ASTRewrite rewrite, Expression expression, Operator operator, int operatorOffset, boolean removeParentheses, Expression[] res) { 1434 if (expression.getStartPosition() + expression.getLength() <= operatorOffset) { 1435 // add to the left 1436 res[0]= combineOperands(rewrite, res[0], expression, removeParentheses, operator); 1437 return; 1438 } 1439 if (operatorOffset <= expression.getStartPosition()) { 1440 // add to the right 1441 res[1]= combineOperands(rewrite, res[1], expression, removeParentheses, operator); 1442 return; 1443 } 1444 if (!(expression instanceof InfixExpression)) { 1445 throw new IllegalArgumentException("Cannot break up non-infix expression"); //$NON-NLS-1$ 1446 } 1447 InfixExpression infixExpression= (InfixExpression) expression; 1448 if (infixExpression.getOperator() != operator) { 1449 throw new IllegalArgumentException("Incompatible operator"); //$NON-NLS-1$ 1450 } 1451 breakInfixOperationAtOperation(rewrite, infixExpression.getLeftOperand(), operator, operatorOffset, removeParentheses, res); 1452 breakInfixOperationAtOperation(rewrite, infixExpression.getRightOperand(), operator, operatorOffset, removeParentheses, res); 1453 1454 List<Expression> extended= infixExpression.extendedOperands(); 1455 for (Expression element : extended) { 1456 breakInfixOperationAtOperation(rewrite, element, operator, operatorOffset, removeParentheses, res); 1457 } 1458 } 1459 combineOperands(ASTRewrite rewrite, Expression existing, Expression originalNode, boolean removeParentheses, Operator operator)1460 private static Expression combineOperands(ASTRewrite rewrite, Expression existing, Expression originalNode, boolean removeParentheses, Operator operator) { 1461 if (existing == null && removeParentheses) { 1462 originalNode= ASTNodes.getUnparenthesedExpression(originalNode); 1463 } 1464 Expression newRight= (Expression)rewrite.createMoveTarget(originalNode); 1465 if (originalNode instanceof InfixExpression) { 1466 ((InfixExpression)newRight).setOperator(((InfixExpression)originalNode).getOperator()); 1467 } 1468 1469 if (existing == null) { 1470 return newRight; 1471 } 1472 AST ast= rewrite.getAST(); 1473 InfixExpression infix= ast.newInfixExpression(); 1474 infix.setOperator(operator); 1475 infix.setLeftOperand(existing); 1476 infix.setRightOperand(newRight); 1477 return infix; 1478 } 1479 getCastAndAssignIfStatementProposals(IInvocationContext context, ASTNode node, Collection<ICommandAccess> resultingCollections)1480 private static boolean getCastAndAssignIfStatementProposals(IInvocationContext context, ASTNode node, Collection<ICommandAccess> resultingCollections) { 1481 if (node instanceof IfStatement) { 1482 node= ((IfStatement)node).getExpression(); 1483 } else if (node instanceof WhileStatement) { 1484 node= ((WhileStatement)node).getExpression(); 1485 } else if (node instanceof Block) { 1486 List<Statement> statements= ((Block)node).statements(); 1487 if (statements.size() > 0) { 1488 if (context.getSelectionOffset() > statements.get(0).getStartPosition()) { 1489 return false; 1490 } 1491 } 1492 ASTNode parent= node.getParent(); 1493 Expression expression= null; 1494 if (parent instanceof IfStatement) { 1495 expression= ((IfStatement)parent).getExpression(); 1496 } else if (parent instanceof WhileStatement) { 1497 expression= ((WhileStatement)parent).getExpression(); 1498 } else { 1499 return false; 1500 } 1501 1502 if (expression instanceof InstanceofExpression) { 1503 node= expression; 1504 } else { 1505 final ArrayList<InstanceofExpression> nodes= new ArrayList<>(); 1506 expression.accept(new ASTVisitor() { 1507 @Override 1508 public boolean visit(InstanceofExpression instanceofExpression) { 1509 nodes.add(instanceofExpression); 1510 return false; 1511 } 1512 }); 1513 1514 if (nodes.size() != 1) { 1515 return false; 1516 } 1517 node= nodes.get(0); 1518 } 1519 } else { 1520 while (node != null && !(node instanceof InstanceofExpression) && !(node instanceof Statement)) { 1521 node= node.getParent(); 1522 } 1523 } 1524 1525 if (!(node instanceof InstanceofExpression)) { 1526 return false; 1527 } 1528 InstanceofExpression expression= (InstanceofExpression)node; 1529 // test that we are the expression of a 'while' or 'if' 1530 while (node.getParent() instanceof Expression) { 1531 node= node.getParent(); 1532 } 1533 StructuralPropertyDescriptor locationInParent= node.getLocationInParent(); 1534 1535 boolean negated= isNegated(expression); 1536 1537 Statement body= null; 1538 ASTNode insertionPosition= null; 1539 if (negated) { 1540 insertionPosition= node.getParent(); 1541 if (locationInParent == IfStatement.EXPRESSION_PROPERTY) { 1542 body= ((IfStatement)node.getParent()).getElseStatement(); 1543 if (body != null) { 1544 negated= false; 1545 } 1546 } 1547 if (body == null && insertionPosition.getParent() instanceof Block) { 1548 body= (Statement)insertionPosition.getParent(); 1549 } 1550 } else { 1551 if (locationInParent == IfStatement.EXPRESSION_PROPERTY) { 1552 body= ((IfStatement)node.getParent()).getThenStatement(); 1553 } else if (locationInParent == WhileStatement.EXPRESSION_PROPERTY) { 1554 body= ((WhileStatement)node.getParent()).getBody(); 1555 } 1556 } 1557 if (body == null) { 1558 return false; 1559 } 1560 1561 Type originalType= expression.getRightOperand(); 1562 if (originalType.resolveBinding() == null) { 1563 return false; 1564 } 1565 1566 // we could produce quick assist 1567 if (resultingCollections == null) { 1568 return true; 1569 } 1570 1571 final String KEY_NAME= "name"; //$NON-NLS-1$ 1572 final String KEY_TYPE= "type"; //$NON-NLS-1$ 1573 // 1574 AST ast= expression.getAST(); 1575 ASTRewrite rewrite= ASTRewrite.create(ast); 1576 ICompilationUnit cu= context.getCompilationUnit(); 1577 // prepare correction proposal 1578 String label= CorrectionMessages.AdvancedQuickAssistProcessor_castAndAssign; 1579 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_LOCAL); 1580 LinkedCorrectionProposal proposal= new LinkedCorrectionProposal(label, cu, rewrite, IProposalRelevance.CAST_AND_ASSIGN, image); 1581 // prepare possible variable names 1582 List<String> excludedNames= Arrays.asList(ASTResolving.getUsedVariableNames(body)); 1583 String[] varNames= suggestLocalVariableNames(cu, originalType.resolveBinding(), excludedNames); 1584 for (String varName : varNames) { 1585 proposal.addLinkedPositionProposal(KEY_NAME, varName, null); 1586 } 1587 CastExpression castExpression= ast.newCastExpression(); 1588 castExpression.setExpression((Expression)rewrite.createCopyTarget(expression.getLeftOperand())); 1589 castExpression.setType((Type)ASTNode.copySubtree(ast, originalType)); 1590 // prepare new variable declaration 1591 VariableDeclarationFragment vdf= ast.newVariableDeclarationFragment(); 1592 vdf.setName(ast.newSimpleName(varNames[0])); 1593 vdf.setInitializer(castExpression); 1594 // prepare new variable declaration statement 1595 VariableDeclarationStatement vds= ast.newVariableDeclarationStatement(vdf); 1596 vds.setType((Type)ASTNode.copySubtree(ast, originalType)); 1597 1598 // add new variable declaration statement 1599 if (negated) { 1600 ListRewrite listRewriter= rewrite.getListRewrite(body, Block.STATEMENTS_PROPERTY); 1601 listRewriter.insertAfter(vds, insertionPosition, null); 1602 } else { 1603 if (body instanceof Block) { 1604 ListRewrite listRewriter= rewrite.getListRewrite(body, Block.STATEMENTS_PROPERTY); 1605 listRewriter.insertAt(vds, 0, null); 1606 } else { 1607 Block newBlock= ast.newBlock(); 1608 List<Statement> statements= newBlock.statements(); 1609 statements.add(vds); 1610 statements.add((Statement) rewrite.createMoveTarget(body)); 1611 rewrite.replace(body, newBlock, null); 1612 } 1613 } 1614 1615 // setup linked positions 1616 proposal.addLinkedPosition(rewrite.track(vdf.getName()), true, KEY_NAME); 1617 proposal.addLinkedPosition(rewrite.track(vds.getType()), false, KEY_TYPE); 1618 proposal.addLinkedPosition(rewrite.track(castExpression.getType()), false, KEY_TYPE); 1619 proposal.setEndPosition(rewrite.track(vds)); // set cursor after expression statement 1620 // add correction proposal 1621 resultingCollections.add(proposal); 1622 return true; 1623 } 1624 isNegated(Expression expression)1625 private static boolean isNegated(Expression expression) { 1626 if (!(expression.getParent() instanceof ParenthesizedExpression)) 1627 return false; 1628 1629 ParenthesizedExpression parenthesis= (ParenthesizedExpression) expression.getParent(); 1630 if (!(parenthesis.getParent() instanceof PrefixExpression)) 1631 return false; 1632 1633 PrefixExpression prefix= (PrefixExpression) parenthesis.getParent(); 1634 if (prefix.getOperator() != PrefixExpression.Operator.NOT) 1635 return false; 1636 1637 return true; 1638 } 1639 suggestLocalVariableNames(ICompilationUnit cu, ITypeBinding binding, List<String> excluded)1640 private static String[] suggestLocalVariableNames(ICompilationUnit cu, ITypeBinding binding, List<String> excluded) { 1641 return StubUtility.getVariableNameSuggestions(NamingConventions.VK_LOCAL, cu.getJavaProject(), binding, null, excluded); 1642 } 1643 getCombineStringProposals(IInvocationContext context, ASTNode node, Collection<ICommandAccess> resultingCollections)1644 private static boolean getCombineStringProposals(IInvocationContext context, ASTNode node, Collection<ICommandAccess> resultingCollections) { 1645 // we work with InfixExpressions 1646 InfixExpression infixExpression; 1647 if (node instanceof InfixExpression) { 1648 infixExpression= (InfixExpression) node; 1649 } else if (node.getParent() instanceof InfixExpression) { 1650 infixExpression= (InfixExpression) node.getParent(); 1651 } else { 1652 return false; 1653 } 1654 1655 1656 // only + is valid for combining strings 1657 if (!infixExpression.getOperator().equals(InfixExpression.Operator.PLUS)) { 1658 return false; 1659 } 1660 1661 // all expressions must be strings 1662 Expression leftOperand= infixExpression.getLeftOperand(); 1663 Expression rightOperand= infixExpression.getRightOperand(); 1664 if (!(leftOperand instanceof StringLiteral) || !(rightOperand instanceof StringLiteral)) { 1665 return false; 1666 } 1667 1668 StringLiteral leftString= (StringLiteral) leftOperand; 1669 StringLiteral rightString= (StringLiteral) rightOperand; 1670 1671 if (resultingCollections == null) { 1672 return true; 1673 } 1674 1675 // begin building combined string 1676 StringBuilder stringBuilder= new StringBuilder(leftString.getLiteralValue()); 1677 stringBuilder.append(rightString.getLiteralValue()); 1678 1679 // append extended string literals 1680 for (Object operand : infixExpression.extendedOperands()) { 1681 if (!(operand instanceof StringLiteral)) 1682 return false; 1683 StringLiteral stringLiteral= (StringLiteral) operand; 1684 stringBuilder.append(stringLiteral.getLiteralValue()); 1685 } 1686 1687 // prepare new string literal 1688 AST ast= node.getAST(); 1689 StringLiteral combinedStringLiteral= ast.newStringLiteral(); 1690 combinedStringLiteral.setLiteralValue(stringBuilder.toString()); 1691 1692 ASTRewrite rewrite= ASTRewrite.create(ast); 1693 rewrite.replace(infixExpression, combinedStringLiteral, null); 1694 1695 // add correction proposal 1696 String label= CorrectionMessages.AdvancedQuickAssistProcessor_combineSelectedStrings; 1697 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 1698 LinkedCorrectionProposal proposal= new LinkedCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.COMBINE_STRINGS, image); 1699 resultingCollections.add(proposal); 1700 return true; 1701 } 1702 getPickOutStringProposals(IInvocationContext context, ASTNode node, Collection<ICommandAccess> resultingCollections)1703 private static boolean getPickOutStringProposals(IInvocationContext context, ASTNode node, Collection<ICommandAccess> resultingCollections) { 1704 // we work with String's 1705 if (!(node instanceof StringLiteral)) { 1706 return false; 1707 } 1708 // user should select part of String 1709 int selectionPos= context.getSelectionOffset(); 1710 int selectionLen= context.getSelectionLength(); 1711 if (selectionLen == 0) { 1712 return false; 1713 } 1714 int valueStart= node.getStartPosition() + 1; 1715 int valueEnd= node.getStartPosition() + node.getLength() - 1; 1716 1717 // selection must be inside node and the quotes and not contain the full value 1718 if (selectionPos < valueStart || selectionPos + selectionLen > valueEnd || valueEnd - valueStart == selectionLen) { 1719 return false; 1720 } 1721 1722 // prepare string parts positions 1723 StringLiteral stringLiteral= (StringLiteral) node; 1724 String stringValue= stringLiteral.getEscapedValue(); 1725 1726 int firstPos= selectionPos - node.getStartPosition(); 1727 int secondPos= firstPos + selectionLen; 1728 1729 1730 // prepare new string literals 1731 1732 AST ast= node.getAST(); 1733 StringLiteral leftLiteral= ast.newStringLiteral(); 1734 StringLiteral centerLiteral= ast.newStringLiteral(); 1735 StringLiteral rightLiteral= ast.newStringLiteral(); 1736 try { 1737 leftLiteral.setEscapedValue('"' + stringValue.substring(1, firstPos) + '"'); 1738 centerLiteral.setEscapedValue('"' + stringValue.substring(firstPos, secondPos) + '"'); 1739 rightLiteral.setEscapedValue('"' + stringValue.substring(secondPos, stringValue.length() - 1) + '"'); 1740 } catch (IllegalArgumentException e) { 1741 return false; 1742 } 1743 if (resultingCollections == null) { 1744 return true; 1745 } 1746 1747 ASTRewrite rewrite= ASTRewrite.create(ast); 1748 1749 // prepare new expression instead of StringLiteral 1750 InfixExpression expression= ast.newInfixExpression(); 1751 expression.setOperator(InfixExpression.Operator.PLUS); 1752 if (firstPos != 1) { 1753 expression.setLeftOperand(leftLiteral); 1754 } 1755 1756 1757 if (firstPos == 1) { 1758 expression.setLeftOperand(centerLiteral); 1759 } else { 1760 expression.setRightOperand(centerLiteral); 1761 } 1762 1763 if (secondPos < stringValue.length() - 1) { 1764 if (firstPos == 1) { 1765 expression.setRightOperand(rightLiteral); 1766 } else { 1767 expression.extendedOperands().add(rightLiteral); 1768 } 1769 } 1770 // use new expression instead of old StirngLiteral 1771 rewrite.replace(stringLiteral, expression, null); 1772 // add correction proposal 1773 String label= CorrectionMessages.AdvancedQuickAssistProcessor_pickSelectedString; 1774 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 1775 LinkedCorrectionProposal proposal= new LinkedCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.PICK_SELECTED_STRING, image); 1776 proposal.addLinkedPosition(rewrite.track(centerLiteral), true, "CENTER_STRING"); //$NON-NLS-1$ 1777 resultingCollections.add(proposal); 1778 return true; 1779 } 1780 getSingleStatement(Statement statement)1781 private static Statement getSingleStatement(Statement statement) { 1782 if (statement instanceof Block) { 1783 List<Statement> blockStatements= ((Block) statement).statements(); 1784 if (blockStatements.size() != 1) { 1785 return null; 1786 } 1787 return blockStatements.get(0); 1788 } 1789 return statement; 1790 } 1791 getReplaceIfElseWithConditionalProposals(IInvocationContext context, ASTNode node, Collection<ICommandAccess> resultingCollections)1792 private static boolean getReplaceIfElseWithConditionalProposals(IInvocationContext context, ASTNode node, Collection<ICommandAccess> resultingCollections) { 1793 if (!(node instanceof IfStatement)) { 1794 return false; 1795 } 1796 IfStatement ifStatement= (IfStatement) node; 1797 Statement thenStatement= getSingleStatement(ifStatement.getThenStatement()); 1798 Statement elseStatement= getSingleStatement(ifStatement.getElseStatement()); 1799 if (thenStatement == null || elseStatement == null) { 1800 return false; 1801 } 1802 Expression assigned= null; 1803 Expression thenExpression= null; 1804 Expression elseExpression= null; 1805 1806 ITypeBinding exprBinding= null; 1807 if (thenStatement instanceof ReturnStatement && elseStatement instanceof ReturnStatement) { 1808 thenExpression= ((ReturnStatement) thenStatement).getExpression(); 1809 elseExpression= ((ReturnStatement) elseStatement).getExpression(); 1810 MethodDeclaration declaration= ASTResolving.findParentMethodDeclaration(node); 1811 if (declaration == null || declaration.isConstructor()) { 1812 return false; 1813 } 1814 exprBinding= declaration.getReturnType2().resolveBinding(); 1815 } else if (thenStatement instanceof ExpressionStatement && elseStatement instanceof ExpressionStatement) { 1816 Expression inner1= ((ExpressionStatement) thenStatement).getExpression(); 1817 Expression inner2= ((ExpressionStatement) elseStatement).getExpression(); 1818 if (inner1 instanceof Assignment && inner2 instanceof Assignment) { 1819 Assignment assign1= (Assignment) inner1; 1820 Assignment assign2= (Assignment) inner2; 1821 Expression left1= assign1.getLeftHandSide(); 1822 Expression left2= assign2.getLeftHandSide(); 1823 if (left1 instanceof Name && left2 instanceof Name && assign1.getOperator() == assign2.getOperator()) { 1824 IBinding bind1= ((Name) left1).resolveBinding(); 1825 IBinding bind2= ((Name) left2).resolveBinding(); 1826 if (bind1 == bind2 && bind1 instanceof IVariableBinding) { 1827 assigned= left1; 1828 exprBinding= ((IVariableBinding) bind1).getType(); 1829 thenExpression= assign1.getRightHandSide(); 1830 elseExpression= assign2.getRightHandSide(); 1831 } 1832 } 1833 } 1834 } 1835 if (thenExpression == null || elseExpression == null) { 1836 return false; 1837 } 1838 1839 // we could produce quick assist 1840 if (resultingCollections == null) { 1841 return true; 1842 } 1843 // 1844 AST ast= node.getAST(); 1845 ASTRewrite rewrite= ASTRewrite.create(ast); 1846 TightSourceRangeComputer sourceRangeComputer= new TightSourceRangeComputer(); 1847 sourceRangeComputer.addTightSourceNode(ifStatement); 1848 rewrite.setTargetSourceRangeComputer(sourceRangeComputer); 1849 1850 String label= CorrectionMessages.AdvancedQuickAssistProcessor_replaceIfWithConditional; 1851 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 1852 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.REPLACE_IF_ELSE_WITH_CONDITIONAL, image); 1853 1854 1855 // prepare conditional expression 1856 ConditionalExpression conditionalExpression= ast.newConditionalExpression(); 1857 Expression conditionCopy= (Expression) rewrite.createCopyTarget(ifStatement.getExpression()); 1858 conditionalExpression.setExpression(conditionCopy); 1859 Expression thenCopy= (Expression) rewrite.createCopyTarget(thenExpression); 1860 Expression elseCopy= (Expression) rewrite.createCopyTarget(elseExpression); 1861 1862 1863 IJavaProject project= context.getCompilationUnit().getJavaProject(); 1864 if (!JavaModelUtil.is50OrHigher(project)) { 1865 ITypeBinding thenBinding= thenExpression.resolveTypeBinding(); 1866 ITypeBinding elseBinding= elseExpression.resolveTypeBinding(); 1867 if (thenBinding != null && elseBinding != null && exprBinding != null && !elseBinding.isAssignmentCompatible(thenBinding)) { 1868 CastExpression castException= ast.newCastExpression(); 1869 ImportRewrite importRewrite= proposal.createImportRewrite(context.getASTRoot()); 1870 ImportRewriteContext importRewriteContext= new ContextSensitiveImportRewriteContext(node, importRewrite); 1871 castException.setType(importRewrite.addImport(exprBinding, ast, importRewriteContext, TypeLocation.CAST)); 1872 castException.setExpression(elseCopy); 1873 elseCopy= castException; 1874 } 1875 } else if (JavaModelUtil.is17OrHigher(project)) { 1876 addExplicitTypeArgumentsIfNecessary(rewrite, proposal, thenExpression); 1877 addExplicitTypeArgumentsIfNecessary(rewrite, proposal, elseExpression); 1878 } 1879 conditionalExpression.setThenExpression(thenCopy); 1880 conditionalExpression.setElseExpression(elseCopy); 1881 1882 // replace 'if' statement with conditional expression 1883 if (assigned == null) { 1884 ReturnStatement returnStatement= ast.newReturnStatement(); 1885 returnStatement.setExpression(conditionalExpression); 1886 rewrite.replace(ifStatement, returnStatement, null); 1887 } else { 1888 Assignment assignment= ast.newAssignment(); 1889 assignment.setLeftHandSide((Expression) rewrite.createCopyTarget(assigned)); 1890 assignment.setRightHandSide(conditionalExpression); 1891 assignment.setOperator(((Assignment) assigned.getParent()).getOperator()); 1892 1893 ExpressionStatement expressionStatement= ast.newExpressionStatement(assignment); 1894 rewrite.replace(ifStatement, expressionStatement, null); 1895 } 1896 1897 // add correction proposal 1898 resultingCollections.add(proposal); 1899 return true; 1900 } 1901 addExplicitTypeArgumentsIfNecessary(ASTRewrite rewrite, ASTRewriteCorrectionProposal proposal, Expression invocation)1902 private static void addExplicitTypeArgumentsIfNecessary(ASTRewrite rewrite, ASTRewriteCorrectionProposal proposal, Expression invocation) { 1903 if (Invocations.isResolvedTypeInferredFromExpectedType(invocation)) { 1904 ITypeBinding[] typeArguments= Invocations.getInferredTypeArguments(invocation); 1905 if (typeArguments == null) 1906 return; 1907 1908 ImportRewrite importRewrite= proposal.getImportRewrite(); 1909 if (importRewrite == null) { 1910 importRewrite= proposal.createImportRewrite((CompilationUnit) invocation.getRoot()); 1911 } 1912 ImportRewriteContext importRewriteContext= new ContextSensitiveImportRewriteContext(invocation, importRewrite); 1913 1914 AST ast= invocation.getAST(); 1915 ListRewrite typeArgsRewrite= Invocations.getInferredTypeArgumentsRewrite(rewrite, invocation); 1916 1917 for (ITypeBinding typeArgument : typeArguments) { 1918 Type typeArgumentNode= importRewrite.addImport(typeArgument, ast, importRewriteContext, TypeLocation.TYPE_ARGUMENT); 1919 typeArgsRewrite.insertLast(typeArgumentNode, null); 1920 } 1921 1922 if (invocation instanceof MethodInvocation) { 1923 MethodInvocation methodInvocation= (MethodInvocation) invocation; 1924 Expression expression= methodInvocation.getExpression(); 1925 if (expression == null) { 1926 IMethodBinding methodBinding= methodInvocation.resolveMethodBinding(); 1927 if (methodBinding != null && Modifier.isStatic(methodBinding.getModifiers())) { 1928 expression= ast.newName(importRewrite.addImport(methodBinding.getDeclaringClass().getTypeDeclaration(), importRewriteContext)); 1929 } else { 1930 expression= ast.newThisExpression(); 1931 } 1932 rewrite.set(invocation, MethodInvocation.EXPRESSION_PROPERTY, expression, null); 1933 } 1934 } 1935 } 1936 } 1937 createReturnExpression(ASTRewrite rewrite, Expression expression)1938 private static ReturnStatement createReturnExpression(ASTRewrite rewrite, Expression expression) { 1939 AST ast= rewrite.getAST(); 1940 ReturnStatement thenReturn= ast.newReturnStatement(); 1941 Expression cleanExpression= expression; 1942 cleanExpression= ASTNodes.getUnparenthesedExpression(cleanExpression); 1943 thenReturn.setExpression((Expression) rewrite.createCopyTarget(cleanExpression)); 1944 return thenReturn; 1945 } 1946 createAssignmentStatement(ASTRewrite rewrite, Assignment.Operator assignmentOperator, Expression origAssignee, Expression origAssigned)1947 private static Statement createAssignmentStatement(ASTRewrite rewrite, Assignment.Operator assignmentOperator, Expression origAssignee, Expression origAssigned) { 1948 AST ast= rewrite.getAST(); 1949 Assignment elseAssignment= ast.newAssignment(); 1950 elseAssignment.setOperator(assignmentOperator); 1951 Expression left= ASTNodes.getUnparenthesedExpression(origAssignee); 1952 Expression right= ASTNodes.getUnparenthesedExpression(origAssigned); 1953 elseAssignment.setLeftHandSide((Expression) rewrite.createCopyTarget(left)); 1954 elseAssignment.setRightHandSide((Expression) rewrite.createCopyTarget(right)); 1955 ExpressionStatement statement= ast.newExpressionStatement(elseAssignment); 1956 return statement; 1957 } 1958 getReplaceConditionalWithIfElseProposals(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections)1959 private static boolean getReplaceConditionalWithIfElseProposals(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections) { 1960 ASTNode node= covering; 1961 while (!(node instanceof ConditionalExpression) && node instanceof Expression) { 1962 node= node.getParent(); 1963 } 1964 if (!(node instanceof ConditionalExpression)) { 1965 node= covering; 1966 while (node != null && !(node instanceof Statement)) { 1967 node= node.getParent(); 1968 } 1969 if (node instanceof VariableDeclarationStatement) { 1970 node= (ASTNode)(((VariableDeclarationStatement)node).fragments().get(0)); 1971 node= ((VariableDeclarationFragment)node).getInitializer(); 1972 } 1973 if (node instanceof ExpressionStatement) { 1974 node= ((ExpressionStatement)node).getExpression(); 1975 if (node instanceof Assignment) { 1976 node= ((Assignment)node).getRightHandSide(); 1977 } 1978 } 1979 if (node instanceof ReturnStatement) { 1980 node= ((ReturnStatement)node).getExpression(); 1981 } 1982 } 1983 1984 if (!(node instanceof ConditionalExpression)) { 1985 return false; 1986 } 1987 1988 ASTNode parentNodeNoParenthesis= node; 1989 StructuralPropertyDescriptor locationInParent= node.getLocationInParent(); 1990 while(locationInParent.getNodeClass() == ParenthesizedExpression.class) { 1991 parentNodeNoParenthesis= parentNodeNoParenthesis.getParent(); 1992 locationInParent= parentNodeNoParenthesis.getLocationInParent(); 1993 } 1994 if (locationInParent == Assignment.RIGHT_HAND_SIDE_PROPERTY) { 1995 if (parentNodeNoParenthesis.getParent().getLocationInParent() != ExpressionStatement.EXPRESSION_PROPERTY) { 1996 return false; 1997 } 1998 } else if (locationInParent == VariableDeclarationFragment.INITIALIZER_PROPERTY) { 1999 ASTNode statement= parentNodeNoParenthesis.getParent().getParent(); 2000 if (!(statement instanceof VariableDeclarationStatement) || statement.getLocationInParent() != Block.STATEMENTS_PROPERTY) { 2001 return false; 2002 } 2003 } else if (locationInParent != ReturnStatement.EXPRESSION_PROPERTY) { 2004 return false; 2005 } 2006 2007 ConditionalExpression conditional= (ConditionalExpression) node; 2008 // we could produce quick assist 2009 if (resultingCollections == null) { 2010 return true; 2011 } 2012 2013 AST ast= node.getAST(); 2014 ASTRewrite rewrite= ASTRewrite.create(ast); 2015 // prepare new 'if' statement 2016 Expression expression= ASTNodes.getUnparenthesedExpression(conditional.getExpression()); 2017 IfStatement ifStatement= ast.newIfStatement(); 2018 ifStatement.setExpression((Expression) rewrite.createCopyTarget(expression)); 2019 2020 if (locationInParent == Assignment.RIGHT_HAND_SIDE_PROPERTY) { 2021 ASTNode replaceNode= node; 2022 while(!(replaceNode instanceof Assignment) && replaceNode != null) { 2023 replaceNode= replaceNode.getParent(); 2024 } 2025 Assignment assignment= (Assignment) replaceNode; 2026 Expression assignee= assignment.getLeftHandSide(); 2027 Assignment.Operator op= assignment.getOperator(); 2028 ifStatement.setThenStatement(createAssignmentStatement(rewrite, op, assignee, conditional.getThenExpression())); 2029 ifStatement.setElseStatement(createAssignmentStatement(rewrite, op, assignee, conditional.getElseExpression())); 2030 rewrite.replace(replaceNode.getParent(), ifStatement, null); 2031 2032 } else if (locationInParent == ReturnStatement.EXPRESSION_PROPERTY) { 2033 ASTNode replaceNode= node; 2034 while(!(replaceNode instanceof ReturnStatement) && replaceNode != null) { 2035 replaceNode= replaceNode.getParent(); 2036 } 2037 ifStatement.setThenStatement(createReturnExpression(rewrite, conditional.getThenExpression())); 2038 ifStatement.setElseStatement(createReturnExpression(rewrite, conditional.getElseExpression())); 2039 // replace return conditional expression with if/then/else/return 2040 rewrite.replace(replaceNode, ifStatement, null); 2041 2042 } else if (locationInParent == VariableDeclarationFragment.INITIALIZER_PROPERTY) { 2043 ASTNode replaceNode= node; 2044 while(!(replaceNode instanceof VariableDeclarationFragment) && replaceNode != null) { 2045 replaceNode= replaceNode.getParent(); 2046 } 2047 VariableDeclarationFragment frag= (VariableDeclarationFragment) replaceNode; 2048 Assignment.Operator op= Assignment.Operator.ASSIGN; 2049 Expression assignee= frag.getName(); 2050 ifStatement.setThenStatement(createAssignmentStatement(rewrite, op, assignee, conditional.getThenExpression())); 2051 ifStatement.setElseStatement(createAssignmentStatement(rewrite, op, assignee, conditional.getElseExpression())); 2052 rewrite.set(frag, VariableDeclarationFragment.INITIALIZER_PROPERTY, null, null); // clear initializer 2053 ASTNode statement= frag.getParent(); 2054 rewrite.getListRewrite(statement.getParent(), Block.STATEMENTS_PROPERTY).insertAfter(ifStatement, statement, null); 2055 } 2056 2057 // add correction proposal 2058 String label= CorrectionMessages.AdvancedQuickAssistProcessor_replaceConditionalWithIf; 2059 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 2060 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.REPLACE_CONDITIONAL_WITH_IF_ELSE, image); 2061 resultingCollections.add(proposal); 2062 return true; 2063 } 2064 getInverseLocalVariableProposals(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections)2065 private static boolean getInverseLocalVariableProposals(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections) { 2066 final AST ast= covering.getAST(); 2067 // cursor should be placed on variable name 2068 if (!(covering instanceof SimpleName)) { 2069 return false; 2070 } 2071 SimpleName coveringName= (SimpleName) covering; 2072 if (!coveringName.isDeclaration()) { 2073 return false; 2074 } 2075 // prepare bindings 2076 final IBinding variableBinding= coveringName.resolveBinding(); 2077 if (!(variableBinding instanceof IVariableBinding)) { 2078 return false; 2079 } 2080 IVariableBinding binding= (IVariableBinding) variableBinding; 2081 if (binding.isField()) { 2082 return false; 2083 } 2084 // we operate only on boolean variable 2085 if (!isBoolean(coveringName)) { 2086 return false; 2087 } 2088 // we could produce quick assist 2089 if (resultingCollections == null) { 2090 return true; 2091 } 2092 // find linked nodes 2093 final BodyDeclaration bodyDecl= ASTResolving.findParentBodyDeclaration(covering); 2094 if (bodyDecl == null) { 2095 return false; 2096 } 2097 SimpleName[] linkedNodes= LinkedNodeFinder.findByBinding(bodyDecl, variableBinding); 2098 // 2099 final ASTRewrite rewrite= ASTRewrite.create(ast); 2100 // create proposal 2101 String label= CorrectionMessages.AdvancedQuickAssistProcessor_inverseBooleanVariable; 2102 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 2103 final String KEY_NAME= "name"; //$NON-NLS-1$ 2104 final LinkedCorrectionProposal proposal= new LinkedCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.INVERSE_BOOLEAN_VARIABLE, image); 2105 // prepare new variable identifier 2106 final String oldIdentifier= coveringName.getIdentifier(); 2107 final String notString= Messages.format(CorrectionMessages.AdvancedQuickAssistProcessor_negatedVariableName, ""); //$NON-NLS-1$ 2108 final String newIdentifier; 2109 if (oldIdentifier.startsWith(notString)) { 2110 int notLength= notString.length(); 2111 if (oldIdentifier.length() > notLength) { 2112 newIdentifier= Character.toLowerCase(oldIdentifier.charAt(notLength)) + oldIdentifier.substring(notLength + 1); 2113 } else { 2114 newIdentifier= oldIdentifier; 2115 } 2116 } else { 2117 newIdentifier= Messages.format(CorrectionMessages.AdvancedQuickAssistProcessor_negatedVariableName, Character.toUpperCase(oldIdentifier.charAt(0)) + oldIdentifier.substring(1)); 2118 } 2119 // 2120 proposal.addLinkedPositionProposal(KEY_NAME, newIdentifier, null); 2121 proposal.addLinkedPositionProposal(KEY_NAME, oldIdentifier, null); 2122 // iterate over linked nodes and replace variable references with negated reference 2123 final HashSet<SimpleName> renamedNames= new HashSet<>(); 2124 for (SimpleName name : linkedNodes) { 2125 if (renamedNames.contains(name)) { 2126 continue; 2127 } 2128 // prepare new name with new identifier 2129 SimpleName newName= ast.newSimpleName(newIdentifier); 2130 proposal.addLinkedPosition(rewrite.track(newName), name == coveringName, KEY_NAME); 2131 // 2132 StructuralPropertyDescriptor location= name.getLocationInParent(); 2133 if (location == SingleVariableDeclaration.NAME_PROPERTY) { 2134 // set new name 2135 rewrite.replace(name, newName, null); 2136 } else if (location == Assignment.LEFT_HAND_SIDE_PROPERTY) { 2137 Assignment assignment= (Assignment) name.getParent(); 2138 Expression expression= assignment.getRightHandSide(); 2139 int exStart= expression.getStartPosition(); 2140 int exEnd= exStart + expression.getLength(); 2141 // collect all names that are used in assignments 2142 HashSet<SimpleName> overlapNames= new HashSet<>(); 2143 for (SimpleName name2 : linkedNodes) { 2144 if (name2 == null) { 2145 continue; 2146 } 2147 int name2Start= name2.getStartPosition(); 2148 if (exStart <= name2Start && name2Start < exEnd) { 2149 overlapNames.add(name2); 2150 } 2151 } 2152 // prepare inverted expression 2153 SimpleNameRenameProvider provider= new SimpleNameRenameProvider() { 2154 @Override 2155 public SimpleName getRenamed(SimpleName simpleName) { 2156 if (simpleName.resolveBinding() == variableBinding) { 2157 renamedNames.add(simpleName); 2158 return ast.newSimpleName(newIdentifier); 2159 } 2160 return null; 2161 } 2162 }; 2163 Expression inversedExpression= getInversedExpression(rewrite, expression, provider); 2164 // if any name was not renamed during expression inverting, we can not already rename it, so fail to create assist 2165 for (SimpleName simpleName : overlapNames) { 2166 Object o= simpleName; 2167 if (!renamedNames.contains(o)) { 2168 return false; 2169 } 2170 } 2171 // check operator and replace if needed 2172 Assignment.Operator operator= assignment.getOperator(); 2173 if (operator == Assignment.Operator.BIT_AND_ASSIGN) { 2174 Assignment newAssignment= ast.newAssignment(); 2175 newAssignment.setLeftHandSide(newName); 2176 newAssignment.setRightHandSide(inversedExpression); 2177 newAssignment.setOperator(Assignment.Operator.BIT_OR_ASSIGN); 2178 rewrite.replace(assignment, newAssignment, null); 2179 } else if (operator == Assignment.Operator.BIT_OR_ASSIGN) { 2180 Assignment newAssignment= ast.newAssignment(); 2181 newAssignment.setLeftHandSide(newName); 2182 newAssignment.setRightHandSide(inversedExpression); 2183 newAssignment.setOperator(Assignment.Operator.BIT_AND_ASSIGN); 2184 rewrite.replace(assignment, newAssignment, null); 2185 } else { 2186 rewrite.replace(expression, inversedExpression, null); 2187 // set new name 2188 rewrite.replace(name, newName, null); 2189 } 2190 } else if (location == VariableDeclarationFragment.NAME_PROPERTY) { 2191 // replace initializer for variable 2192 VariableDeclarationFragment vdf= (VariableDeclarationFragment) name.getParent(); 2193 Expression expression= vdf.getInitializer(); 2194 if (expression != null) { 2195 rewrite.replace(expression, getInversedExpression(rewrite, expression), null); 2196 } 2197 // set new name 2198 rewrite.replace(name, newName, null); 2199 } else if (name.getParent() instanceof PrefixExpression && ((PrefixExpression) name.getParent()).getOperator() == PrefixExpression.Operator.NOT) { 2200 rewrite.replace(name.getParent(), newName, null); 2201 } else { 2202 PrefixExpression expression= ast.newPrefixExpression(); 2203 expression.setOperator(PrefixExpression.Operator.NOT); 2204 expression.setOperand(newName); 2205 rewrite.replace(name, expression, null); 2206 } 2207 } 2208 // add correction proposal 2209 resultingCollections.add(proposal); 2210 return true; 2211 } 2212 getPushNegationDownProposals(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections)2213 private static boolean getPushNegationDownProposals(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections) { 2214 PrefixExpression negationExpression= null; 2215 ParenthesizedExpression parenthesizedExpression= null; 2216 // check for case when cursor is on '!' before parentheses 2217 if (covering instanceof PrefixExpression) { 2218 PrefixExpression prefixExpression= (PrefixExpression) covering; 2219 if (prefixExpression.getOperator() == PrefixExpression.Operator.NOT && prefixExpression.getOperand() instanceof ParenthesizedExpression) { 2220 negationExpression= prefixExpression; 2221 parenthesizedExpression= (ParenthesizedExpression) prefixExpression.getOperand(); 2222 } 2223 } 2224 // check for case when cursor is on parenthesized expression that is negated 2225 if (covering instanceof ParenthesizedExpression && covering.getParent() instanceof PrefixExpression && ((PrefixExpression) covering.getParent()).getOperator() == PrefixExpression.Operator.NOT) { 2226 negationExpression= (PrefixExpression) covering.getParent(); 2227 parenthesizedExpression= (ParenthesizedExpression) covering; 2228 } 2229 if (negationExpression == null || (!(parenthesizedExpression.getExpression() instanceof InfixExpression) && !(parenthesizedExpression.getExpression() instanceof ConditionalExpression))) { 2230 return false; 2231 } 2232 // we could produce quick assist 2233 if (resultingCollections == null) { 2234 return true; 2235 } 2236 // 2237 final AST ast= covering.getAST(); 2238 final ASTRewrite rewrite= ASTRewrite.create(ast); 2239 // prepared inverted expression 2240 Expression inversedExpression= getInversedExpression(rewrite, parenthesizedExpression.getExpression()); 2241 // check, may be we should keep parentheses 2242 boolean keepParentheses= false; 2243 if (negationExpression.getParent() instanceof Expression) { 2244 int parentPrecedence= OperatorPrecedence.getExpressionPrecedence(((Expression) negationExpression.getParent())); 2245 int inversedExpressionPrecedence= OperatorPrecedence.getExpressionPrecedence(inversedExpression); 2246 keepParentheses= parentPrecedence > inversedExpressionPrecedence; 2247 } 2248 // replace negated expression with inverted one 2249 if (keepParentheses) { 2250 ParenthesizedExpression pe= ast.newParenthesizedExpression(); 2251 pe.setExpression(inversedExpression); 2252 rewrite.replace(negationExpression, pe, null); 2253 } else { 2254 rewrite.replace(negationExpression, inversedExpression, null); 2255 } 2256 // add correction proposal 2257 String label= CorrectionMessages.AdvancedQuickAssistProcessor_pushNegationDown; 2258 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 2259 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.PULL_NEGATION_DOWN, image); 2260 resultingCollections.add(proposal); 2261 return true; 2262 } 2263 getBooleanExpression(ASTNode node)2264 private static Expression getBooleanExpression(ASTNode node) { 2265 if (!(node instanceof Expression)) { 2266 return null; 2267 } 2268 2269 // check if the node is a location where it can be negated 2270 StructuralPropertyDescriptor locationInParent= node.getLocationInParent(); 2271 if (locationInParent == QualifiedName.NAME_PROPERTY) { 2272 node= node.getParent(); 2273 locationInParent= node.getLocationInParent(); 2274 } 2275 while (locationInParent == ParenthesizedExpression.EXPRESSION_PROPERTY) { 2276 node= node.getParent(); 2277 locationInParent= node.getLocationInParent(); 2278 } 2279 Expression expression= (Expression) node; 2280 if (!isBoolean(expression)) { 2281 return null; 2282 } 2283 if (expression.getParent() instanceof InfixExpression) { 2284 return expression; 2285 } 2286 if (locationInParent == Assignment.RIGHT_HAND_SIDE_PROPERTY || locationInParent == IfStatement.EXPRESSION_PROPERTY 2287 || locationInParent == WhileStatement.EXPRESSION_PROPERTY || locationInParent == DoStatement.EXPRESSION_PROPERTY 2288 || locationInParent == ReturnStatement.EXPRESSION_PROPERTY || locationInParent == ForStatement.EXPRESSION_PROPERTY 2289 || locationInParent == AssertStatement.EXPRESSION_PROPERTY || locationInParent == MethodInvocation.ARGUMENTS_PROPERTY 2290 || locationInParent == ConstructorInvocation.ARGUMENTS_PROPERTY || locationInParent == SuperMethodInvocation.ARGUMENTS_PROPERTY 2291 || locationInParent == EnumConstantDeclaration.ARGUMENTS_PROPERTY || locationInParent == SuperConstructorInvocation.ARGUMENTS_PROPERTY 2292 || locationInParent == ClassInstanceCreation.ARGUMENTS_PROPERTY || locationInParent == ConditionalExpression.EXPRESSION_PROPERTY 2293 || locationInParent == PrefixExpression.OPERAND_PROPERTY) { 2294 return expression; 2295 } 2296 return null; 2297 } 2298 getPullNegationUpProposals(IInvocationContext context, ArrayList<ASTNode> coveredNodes, Collection<ICommandAccess> resultingCollections)2299 private static boolean getPullNegationUpProposals(IInvocationContext context, ArrayList<ASTNode> coveredNodes, Collection<ICommandAccess> resultingCollections) { 2300 if (coveredNodes.size() != 1) { 2301 return false; 2302 } 2303 // 2304 ASTNode fullyCoveredNode= coveredNodes.get(0); 2305 2306 Expression expression= getBooleanExpression(fullyCoveredNode); 2307 if (expression == null || (!(expression instanceof InfixExpression) && !(expression instanceof ConditionalExpression))) { 2308 return false; 2309 } 2310 // we could produce quick assist 2311 if (resultingCollections == null) { 2312 return true; 2313 } 2314 // 2315 AST ast= expression.getAST(); 2316 final ASTRewrite rewrite= ASTRewrite.create(ast); 2317 // prepared inverted expression 2318 Expression inversedExpression= getInversedExpression(rewrite, expression); 2319 // prepare ParenthesizedExpression 2320 ParenthesizedExpression parenthesizedExpression= ast.newParenthesizedExpression(); 2321 parenthesizedExpression.setExpression(inversedExpression); 2322 // prepare NOT prefix expression 2323 PrefixExpression prefixExpression= ast.newPrefixExpression(); 2324 prefixExpression.setOperator(PrefixExpression.Operator.NOT); 2325 prefixExpression.setOperand(parenthesizedExpression); 2326 // replace old expression 2327 rewrite.replace(expression, prefixExpression, null); 2328 // add correction proposal 2329 String label= CorrectionMessages.AdvancedQuickAssistProcessor_pullNegationUp; 2330 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 2331 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.PULL_NEGATION_UP, image); 2332 resultingCollections.add(proposal); 2333 return true; 2334 } 2335 getJoinIfListInIfElseIfProposals(IInvocationContext context, ASTNode covering, ArrayList<ASTNode> coveredNodes, Collection<ICommandAccess> resultingCollections)2336 private static boolean getJoinIfListInIfElseIfProposals(IInvocationContext context, ASTNode covering, ArrayList<ASTNode> coveredNodes, Collection<ICommandAccess> resultingCollections) { 2337 if (coveredNodes.isEmpty()) { 2338 return false; 2339 } 2340 // check that we have more than one covered statement 2341 if (coveredNodes.size() < 2) { 2342 return false; 2343 } 2344 // check that all selected nodes are 'if' statements with only 'then' statement 2345 for (ASTNode node : coveredNodes) { 2346 if (!(node instanceof IfStatement)) { 2347 return false; 2348 } 2349 IfStatement ifStatement= (IfStatement) node; 2350 if (ifStatement.getElseStatement() != null) { 2351 return false; 2352 } 2353 } 2354 // we could produce quick assist 2355 if (resultingCollections == null) { 2356 return true; 2357 } 2358 // 2359 final AST ast= covering.getAST(); 2360 final ASTRewrite rewrite= ASTRewrite.create(ast); 2361 // 2362 IfStatement firstIfStatement= (IfStatement) coveredNodes.get(0); 2363 IfStatement firstNewIfStatement= null; 2364 // 2365 IfStatement prevIfStatement= null; 2366 for (ASTNode astNode : coveredNodes) { 2367 IfStatement ifStatement= (IfStatement) astNode; 2368 // prepare new 'if' statement 2369 IfStatement newIfStatement= ast.newIfStatement(); 2370 newIfStatement.setExpression((Expression) rewrite.createMoveTarget(ifStatement.getExpression())); 2371 // prepare 'then' statement and convert into block if needed 2372 Statement thenStatement= (Statement) rewrite.createMoveTarget(ifStatement.getThenStatement()); 2373 if (ifStatement.getThenStatement() instanceof IfStatement) { 2374 IfStatement ifBodyStatement= (IfStatement) ifStatement.getThenStatement(); 2375 if (ifBodyStatement.getElseStatement() == null) { 2376 Block thenBlock= ast.newBlock(); 2377 thenBlock.statements().add(thenStatement); 2378 thenStatement= thenBlock; 2379 } 2380 } 2381 newIfStatement.setThenStatement(thenStatement); 2382 // 2383 if (prevIfStatement != null) { 2384 prevIfStatement.setElseStatement(newIfStatement); 2385 rewrite.remove(ifStatement, null); 2386 } else { 2387 firstNewIfStatement= newIfStatement; 2388 } 2389 prevIfStatement= newIfStatement; 2390 } 2391 rewrite.replace(firstIfStatement, firstNewIfStatement, null); 2392 // add correction proposal 2393 String label= CorrectionMessages.AdvancedQuickAssistProcessor_joinIfSequence; 2394 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); 2395 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.JOIN_IF_SEQUENCE, image); 2396 resultingCollections.add(proposal); 2397 return true; 2398 } 2399 getConvertSwitchToIfProposals(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections)2400 private static boolean getConvertSwitchToIfProposals(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections) { 2401 if (!(covering instanceof SwitchStatement)) { 2402 return false; 2403 } 2404 // we could produce quick assist (if all 'case' statements end with 'break') 2405 if (resultingCollections == null) { 2406 return true; 2407 } 2408 if (!getConvertSwitchToIfProposals(context, covering, resultingCollections, false)) 2409 return false; 2410 return getConvertSwitchToIfProposals(context, covering, resultingCollections, true); 2411 } 2412 getConvertSwitchToIfProposals(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections, boolean preserveNPE)2413 private static boolean getConvertSwitchToIfProposals(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections, boolean preserveNPE) { 2414 final AST ast= covering.getAST(); 2415 final ASTRewrite rewrite= ASTRewrite.create(ast); 2416 final ImportRewrite importRewrite= StubUtility.createImportRewrite(context.getASTRoot(), true); 2417 // 2418 SwitchStatement switchStatement= (SwitchStatement) covering; 2419 ITypeBinding expressionType= switchStatement.getExpression().resolveTypeBinding(); 2420 boolean isStringsInSwitch= expressionType != null && "java.lang.String".equals(expressionType.getQualifiedName()); //$NON-NLS-1$ 2421 2422 if (!isStringsInSwitch && preserveNPE) 2423 return false; 2424 2425 IfStatement firstIfStatement= null; 2426 IfStatement currentIfStatement= null; 2427 Block currentBlock= null; 2428 boolean hasStopAsLastExecutableStatement= false; 2429 Block defaultBlock= null; 2430 Expression currentCondition= null; 2431 boolean defaultFound= false; 2432 2433 ArrayList<Block> allBlocks= new ArrayList<>(); 2434 ImportRewriteContext importRewriteContext= new ContextSensitiveImportRewriteContext(ASTResolving.findParentBodyDeclaration(covering), importRewrite); 2435 2436 Expression switchExpression= switchStatement.getExpression(); 2437 Name varName; 2438 VariableDeclarationStatement variableDeclarationStatement= null; 2439 if (switchExpression instanceof Name) { 2440 varName= (Name) switchExpression; 2441 } else { 2442 // Switch expression could have side effects, see bug 252040 2443 VariableDeclarationFragment variableDeclarationFragment= ast.newVariableDeclarationFragment(); 2444 String[] varNames= StubUtility.getVariableNameSuggestions(NamingConventions.VK_LOCAL, context.getCompilationUnit().getJavaProject(), expressionType, switchExpression, null); 2445 varName= ast.newSimpleName(varNames[0]); 2446 variableDeclarationFragment.setName((SimpleName) varName); 2447 variableDeclarationFragment.setStructuralProperty(VariableDeclarationFragment.INITIALIZER_PROPERTY, rewrite.createCopyTarget(switchExpression)); 2448 2449 variableDeclarationStatement= ast.newVariableDeclarationStatement(variableDeclarationFragment); 2450 Type type= importRewrite.addImport(expressionType, ast, importRewriteContext, TypeLocation.LOCAL_VARIABLE); 2451 variableDeclarationStatement.setType(type); 2452 } 2453 2454 for (Iterator<Statement> iter= switchStatement.statements().iterator(); iter.hasNext();) { 2455 Statement statement= iter.next(); 2456 if (statement instanceof SwitchCase) { 2457 SwitchCase switchCase= (SwitchCase) statement; 2458 if (ASTHelper.isSwitchCaseExpressionsSupportedInAST(ast) && switchCase.expressions().size() > 1) { 2459 return false; 2460 } 2461 // special case: pass through 2462 if (currentBlock != null) { 2463 if (!hasStopAsLastExecutableStatement) { 2464 return false; 2465 } 2466 currentBlock= null; 2467 } 2468 2469 if (defaultFound) { 2470 // This gets too complicated. We only support 'default' as last SwitchCase. 2471 return false; 2472 } 2473 if (switchCase.isDefault()) { 2474 defaultFound= true; 2475 } 2476 // prepare condition (is null for 'default') 2477 Expression switchCaseCondition= createSwitchCaseCondition(ast, rewrite, importRewrite, importRewriteContext, varName, switchCase, isStringsInSwitch, preserveNPE); 2478 if (currentCondition == null) { 2479 currentCondition= switchCaseCondition; 2480 } else { 2481 InfixExpression condition= ast.newInfixExpression(); 2482 condition.setOperator(InfixExpression.Operator.CONDITIONAL_OR); 2483 condition.setLeftOperand(currentCondition); 2484 if (switchCaseCondition == null) 2485 switchCaseCondition= ast.newBooleanLiteral(true); 2486 condition.setRightOperand(switchCaseCondition); 2487 currentCondition= condition; 2488 } 2489 } else { 2490 // ensure that current block exists as 'then' statement of 'if' 2491 if (currentBlock == null) { 2492 if (currentCondition != null) { 2493 IfStatement ifStatement; 2494 if (firstIfStatement == null) { 2495 firstIfStatement= ast.newIfStatement(); 2496 ifStatement= firstIfStatement; 2497 } else { 2498 ifStatement= ast.newIfStatement(); 2499 currentIfStatement.setElseStatement(ifStatement); 2500 } 2501 currentIfStatement= ifStatement; 2502 ifStatement.setExpression(currentCondition); 2503 currentCondition= null; 2504 currentBlock= ast.newBlock(); 2505 ifStatement.setThenStatement(currentBlock); 2506 allBlocks.add(currentBlock); 2507 } else { 2508 // case for default: 2509 defaultBlock= ast.newBlock(); 2510 currentBlock= defaultBlock; 2511 allBlocks.add(currentBlock); 2512 // delay adding of default block 2513 } 2514 } 2515 if (statement instanceof BreakStatement) { 2516 currentBlock= null; 2517 } else { 2518 // add current statement in current block 2519 2520 hasStopAsLastExecutableStatement= hasStopAsLastExecutableStatement(statement); 2521 Statement copyStatement= copyStatementExceptBreak(ast, rewrite, statement); 2522 2523 currentBlock.statements().add(copyStatement); 2524 } 2525 } 2526 } 2527 // check, may be we have delayed default block 2528 if (defaultBlock != null) { 2529 currentIfStatement.setElseStatement(defaultBlock); 2530 } 2531 // remove unnecessary blocks in blocks 2532 for (Block block : allBlocks) { 2533 List<Statement> statements= block.statements(); 2534 if (statements.size() == 1 && statements.get(0) instanceof Block) { 2535 Block innerBlock= (Block) statements.remove(0); 2536 block.getParent().setStructuralProperty(block.getLocationInParent(), innerBlock); 2537 } 2538 } 2539 2540 if (variableDeclarationStatement == null) { 2541 // replace 'switch' with single if-else-if statement 2542 rewrite.replace(switchStatement, firstIfStatement, null); 2543 } else { 2544 new StatementRewrite(rewrite, new ASTNode[] { switchStatement }).replace(new ASTNode[] { variableDeclarationStatement, firstIfStatement }, null); 2545 } 2546 2547 // add correction proposal 2548 String source= ASTNodes.asString(switchExpression).replaceAll("\r\n?|\n", " "); //$NON-NLS-1$ //$NON-NLS-2$ 2549 String label= preserveNPE ? Messages.format(CorrectionMessages.AdvancedQuickAssistProcessor_convertSwitchToIf_preserveNPE, source) : CorrectionMessages.AdvancedQuickAssistProcessor_convertSwitchToIf; 2550 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.CONVERT_SWITCH_TO_IF_ELSE); 2551 proposal.setImportRewrite(importRewrite); 2552 resultingCollections.add(proposal); 2553 return true; 2554 } 2555 createSwitchCaseCondition(AST ast, ASTRewrite rewrite, ImportRewrite importRewrite, ImportRewriteContext importRewriteContext, Name switchExpression, SwitchCase switchCase, boolean isStringsInSwitch, boolean preserveNPE)2556 private static Expression createSwitchCaseCondition(AST ast, ASTRewrite rewrite, ImportRewrite importRewrite, ImportRewriteContext importRewriteContext, Name switchExpression, 2557 SwitchCase switchCase, boolean isStringsInSwitch, boolean preserveNPE) { 2558 Expression expression= null; 2559 if (ASTHelper.isSwitchCaseExpressionsSupportedInAST(ast)) { 2560 if (switchCase.expressions().size() == 1) { 2561 expression= (Expression) switchCase.expressions().get(0); 2562 } 2563 } else { 2564 expression= switchCase.getExpression(); 2565 } 2566 if (expression == null) 2567 return null; 2568 2569 if (isStringsInSwitch) { 2570 MethodInvocation methodInvocation= ast.newMethodInvocation(); 2571 methodInvocation.setName(ast.newSimpleName("equals")); //$NON-NLS-1$ 2572 if (preserveNPE) { 2573 methodInvocation.setExpression((Expression) rewrite.createStringPlaceholder(switchExpression.getFullyQualifiedName(), ASTNode.QUALIFIED_NAME)); 2574 methodInvocation.arguments().add(rewrite.createCopyTarget(expression)); 2575 } else { 2576 methodInvocation.setExpression((Expression) rewrite.createCopyTarget(expression)); 2577 methodInvocation.arguments().add(rewrite.createStringPlaceholder(switchExpression.getFullyQualifiedName(), ASTNode.QUALIFIED_NAME)); 2578 } 2579 return methodInvocation; 2580 } else { 2581 InfixExpression condition= ast.newInfixExpression(); 2582 condition.setOperator(InfixExpression.Operator.EQUALS); 2583 condition.setLeftOperand((Expression) rewrite.createStringPlaceholder(switchExpression.getFullyQualifiedName(), ASTNode.QUALIFIED_NAME)); 2584 2585 Expression rightExpression= null; 2586 if (expression instanceof SimpleName && ((SimpleName) expression).resolveBinding() instanceof IVariableBinding) { 2587 IVariableBinding binding= (IVariableBinding) ((SimpleName) expression).resolveBinding(); 2588 if (binding.isEnumConstant()) { 2589 String qualifiedName= importRewrite.addImport(binding.getDeclaringClass(), importRewriteContext) + '.' + binding.getName(); 2590 rightExpression= ast.newName(qualifiedName); 2591 } 2592 } 2593 if (rightExpression == null) { 2594 rightExpression= (Expression) rewrite.createCopyTarget(expression); 2595 } 2596 condition.setRightOperand(rightExpression); 2597 return condition; 2598 } 2599 } 2600 2601 hasStopAsLastExecutableStatement(Statement lastStatement)2602 private static boolean hasStopAsLastExecutableStatement(Statement lastStatement) { 2603 if (lastStatement instanceof ReturnStatement || lastStatement instanceof BreakStatement) { 2604 return true; 2605 } 2606 if (lastStatement instanceof Block) { 2607 Block block= (Block) lastStatement; 2608 lastStatement= (Statement) block.statements().get(block.statements().size() - 1); 2609 return hasStopAsLastExecutableStatement(lastStatement); 2610 } 2611 return false; 2612 } 2613 copyStatementExceptBreak(AST ast, ASTRewrite rewrite, Statement source)2614 private static Statement copyStatementExceptBreak(AST ast, ASTRewrite rewrite, Statement source) { 2615 if (source instanceof Block) { 2616 Block block= (Block) source; 2617 Block newBlock= ast.newBlock(); 2618 for (Iterator<Statement> iter= block.statements().iterator(); iter.hasNext();) { 2619 Statement statement= iter.next(); 2620 if (statement instanceof BreakStatement) { 2621 continue; 2622 } 2623 newBlock.statements().add(copyStatementExceptBreak(ast, rewrite, statement)); 2624 } 2625 return newBlock; 2626 } 2627 return (Statement) rewrite.createMoveTarget(source); 2628 } 2629 getConvertIfElseToSwitchProposals(IInvocationContext context, ASTNode coveringNode, ArrayList<ICommandAccess> resultingCollections)2630 private static boolean getConvertIfElseToSwitchProposals(IInvocationContext context, ASTNode coveringNode, ArrayList<ICommandAccess> resultingCollections) { 2631 if (!(coveringNode instanceof IfStatement)) { 2632 return false; 2633 } 2634 // we could produce quick assist 2635 if (resultingCollections == null) { 2636 return true; 2637 } 2638 2639 if(!getConvertIfElseToSwitchProposals(context, coveringNode, resultingCollections, true)) 2640 return false; 2641 return getConvertIfElseToSwitchProposals(context, coveringNode, resultingCollections, false); 2642 } 2643 getConvertIfElseToSwitchProposals(IInvocationContext context, ASTNode coveringNode, ArrayList<ICommandAccess> resultingCollections, boolean handleNullArg)2644 private static boolean getConvertIfElseToSwitchProposals(IInvocationContext context, ASTNode coveringNode, ArrayList<ICommandAccess> resultingCollections, boolean handleNullArg) { 2645 final AST ast= coveringNode.getAST(); 2646 final ASTRewrite rewrite= ASTRewrite.create(ast); 2647 final ImportRewrite importRewrite= StubUtility.createImportRewrite(context.getASTRoot(), true); 2648 ImportRewriteContext importRewriteContext= new ContextSensitiveImportRewriteContext(ASTResolving.findParentBodyDeclaration(coveringNode), importRewrite); 2649 IfStatement ifStatement= (IfStatement) coveringNode; 2650 IfStatement currentIf= ifStatement; 2651 Statement currentStatement= ifStatement; 2652 Expression currentExpression= currentIf.getExpression(); 2653 SwitchStatement switchStatement= ast.newSwitchStatement(); 2654 Expression switchExpression= null; 2655 boolean executeDefaultOnNullExpression= false; 2656 Statement defaultStatement=null; 2657 2658 while (currentStatement != null) { 2659 Expression expression= null; 2660 List<Expression> caseExpressions= new ArrayList<>(); 2661 if (currentIf != null) { 2662 while (currentExpression != null) { // loop for fall through cases - multiple expressions with || operator 2663 Expression leftOperand; 2664 Expression rightOperand; 2665 boolean isMethodInvocationCase= false; 2666 if (currentExpression instanceof MethodInvocation) { 2667 isMethodInvocationCase= true; 2668 if (!(((MethodInvocation) currentExpression).getName().getIdentifier()).equals("equals")) //$NON-NLS-1$ 2669 return false; 2670 2671 MethodInvocation invocation= (MethodInvocation) currentExpression; 2672 leftOperand= invocation.getExpression(); 2673 if (leftOperand == null) 2674 return false; 2675 ITypeBinding leftBinding= leftOperand.resolveTypeBinding(); 2676 if (leftBinding != null) { 2677 if (leftBinding.getQualifiedName().equals("java.lang.String")) { //$NON-NLS-1$ 2678 if (!JavaModelUtil.is17OrHigher(context.getCompilationUnit().getJavaProject())) 2679 return false; 2680 } else if (!leftBinding.isEnum()) { 2681 return false; 2682 } 2683 } 2684 2685 List<Expression> arguments= invocation.arguments(); 2686 if (arguments.size() != 1) 2687 return false; 2688 rightOperand= arguments.get(0); 2689 ITypeBinding rightBinding= leftOperand.resolveTypeBinding(); 2690 if (rightBinding != null) { 2691 if (rightBinding.getQualifiedName().equals("java.lang.String")) { //$NON-NLS-1$ 2692 if (!JavaModelUtil.is17OrHigher(context.getCompilationUnit().getJavaProject())) 2693 return false; 2694 } else if (!rightBinding.isEnum()) { 2695 return false; 2696 } 2697 } 2698 2699 2700 } else if (currentExpression instanceof InfixExpression) { 2701 InfixExpression infixExpression= (InfixExpression) currentExpression; 2702 Operator operator= infixExpression.getOperator(); 2703 if (!operator.equals(InfixExpression.Operator.CONDITIONAL_OR) 2704 && !operator.equals(InfixExpression.Operator.EQUALS)) 2705 return false; 2706 2707 leftOperand= infixExpression.getLeftOperand(); 2708 rightOperand= infixExpression.getRightOperand(); 2709 2710 if (operator.equals(InfixExpression.Operator.EQUALS)) { 2711 ITypeBinding typeBinding= leftOperand.resolveTypeBinding(); 2712 if (typeBinding != null && typeBinding.getQualifiedName().equals("java.lang.String")) { //$NON-NLS-1$ 2713 return false; // don't propose quick assist when == is used to compare strings, since switch will use equals() 2714 } 2715 } else if (operator.equals(InfixExpression.Operator.CONDITIONAL_OR)) { 2716 currentExpression= leftOperand; 2717 continue; 2718 } 2719 } else { 2720 return false; 2721 } 2722 2723 if (leftOperand.resolveConstantExpressionValue() != null) { 2724 caseExpressions.add(leftOperand); 2725 expression= rightOperand; 2726 executeDefaultOnNullExpression|= isMethodInvocationCase; 2727 } else if (rightOperand.resolveConstantExpressionValue() != null) { 2728 caseExpressions.add(rightOperand); 2729 expression= leftOperand; 2730 } else if (leftOperand instanceof QualifiedName) { 2731 QualifiedName qualifiedName= (QualifiedName) leftOperand; 2732 IVariableBinding binding= (IVariableBinding) qualifiedName.resolveBinding(); 2733 if (binding == null || !binding.isEnumConstant()) 2734 return false; 2735 importRewrite.addImport(binding.getDeclaringClass(), importRewriteContext); 2736 caseExpressions.add(qualifiedName.getName()); 2737 expression= rightOperand; 2738 executeDefaultOnNullExpression|= isMethodInvocationCase; 2739 } else if (rightOperand instanceof QualifiedName) { 2740 QualifiedName qualifiedName= (QualifiedName) rightOperand; 2741 IVariableBinding binding= (IVariableBinding) qualifiedName.resolveBinding(); 2742 if (binding == null || !binding.isEnumConstant()) 2743 return false; 2744 importRewrite.addImport(binding.getDeclaringClass(), importRewriteContext); 2745 caseExpressions.add(qualifiedName.getName()); 2746 expression= leftOperand; 2747 } else { 2748 return false; 2749 } 2750 if (expression == null) { // paranoidal check: this condition should never be true 2751 return false; 2752 } 2753 2754 if (currentExpression.getParent() instanceof InfixExpression) { 2755 currentExpression= getNextSiblingExpression(currentExpression); 2756 } else { 2757 currentExpression= null; 2758 } 2759 2760 if (switchExpression == null) { 2761 switchExpression= expression; 2762 } 2763 2764 if (!switchExpression.subtreeMatch(new ASTMatcher(), expression)) { 2765 return false; 2766 } 2767 } 2768 } 2769 2770 Statement thenStatement; 2771 if (currentIf == null) { 2772 thenStatement= currentStatement; //currentStatement has the default else block 2773 defaultStatement= currentStatement; 2774 } else { 2775 thenStatement= currentIf.getThenStatement(); 2776 } 2777 2778 for (SwitchCase switchCaseStatement : createSwitchCaseStatements(ast, rewrite, caseExpressions)) { 2779 switchStatement.statements().add(switchCaseStatement); 2780 } 2781 boolean isBreakRequired= true; 2782 if (thenStatement instanceof Block) { 2783 Statement statement= null; 2784 for (Iterator<Statement> iter= ((Block) thenStatement).statements().iterator(); iter.hasNext();) { 2785 statement= iter.next(); 2786 switchStatement.statements().add(rewrite.createCopyTarget(statement)); 2787 } 2788 if (statement instanceof ReturnStatement || statement instanceof ThrowStatement) 2789 isBreakRequired= false; 2790 } else { 2791 if (thenStatement instanceof ReturnStatement || thenStatement instanceof ThrowStatement) 2792 isBreakRequired= false; 2793 switchStatement.statements().add(rewrite.createCopyTarget(thenStatement)); 2794 } 2795 if (isBreakRequired) 2796 switchStatement.statements().add(ast.newBreakStatement()); 2797 2798 // advance currentStatement to the next "else if" or "else": 2799 if (currentIf != null && currentIf.getElseStatement() != null) { 2800 Statement elseStatement= currentIf.getElseStatement(); 2801 if (elseStatement instanceof IfStatement) { 2802 currentIf= (IfStatement) elseStatement; 2803 currentStatement= currentIf; 2804 currentExpression= currentIf.getExpression(); 2805 } else { 2806 currentIf= null; 2807 currentStatement= elseStatement; 2808 currentExpression= null; 2809 } 2810 } else { 2811 currentStatement= null; 2812 } 2813 } 2814 2815 if (switchExpression == null) 2816 return false; 2817 switchStatement.setExpression((Expression) rewrite.createCopyTarget(switchExpression)); 2818 2819 if (handleNullArg) { 2820 if (executeDefaultOnNullExpression) { 2821 IfStatement newIfStatement= ast.newIfStatement(); 2822 2823 InfixExpression infixExpression= ast.newInfixExpression(); 2824 infixExpression.setLeftOperand((Expression) rewrite.createCopyTarget(switchExpression)); 2825 infixExpression.setRightOperand(ast.newNullLiteral()); 2826 infixExpression.setOperator(InfixExpression.Operator.EQUALS); 2827 newIfStatement.setExpression(infixExpression); 2828 2829 if (defaultStatement == null) { 2830 Block block= ast.newBlock(); 2831 newIfStatement.setThenStatement(block); 2832 } else if (defaultStatement instanceof Block) { 2833 Block block= ast.newBlock(); 2834 for (Iterator<Statement> iter= ((Block) defaultStatement).statements().iterator(); iter.hasNext();) { 2835 block.statements().add(rewrite.createCopyTarget(iter.next())); 2836 } 2837 newIfStatement.setThenStatement(block); 2838 } else { 2839 newIfStatement.setThenStatement((Statement) rewrite.createCopyTarget(defaultStatement)); 2840 } 2841 Block block= ast.newBlock(); 2842 block.statements().add(switchStatement); 2843 newIfStatement.setElseStatement(block); 2844 2845 rewrite.replace(ifStatement, newIfStatement, null); 2846 2847 String source= ASTNodes.asString(switchExpression).replaceAll("\r\n?|\n", " "); //$NON-NLS-1$ //$NON-NLS-2$ 2848 String label= Messages.format(CorrectionMessages.AdvancedQuickAssistProcessor_convertIfElseToSwitch_handleNullArg, source); 2849 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.CONVERT_IF_ELSE_TO_SWITCH); 2850 proposal.setImportRewrite(importRewrite); 2851 resultingCollections.add(proposal); 2852 } 2853 } else { 2854 rewrite.replace(ifStatement, switchStatement, null); 2855 2856 String label= CorrectionMessages.AdvancedQuickAssistProcessor_convertIfElseToSwitch; 2857 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.CONVERT_IF_ELSE_TO_SWITCH); 2858 proposal.setImportRewrite(importRewrite); 2859 resultingCollections.add(proposal); 2860 } 2861 return true; 2862 } 2863 getNextSiblingExpression(Expression expression)2864 private static Expression getNextSiblingExpression(Expression expression) { 2865 InfixExpression parentInfixExpression= (InfixExpression) expression.getParent(); 2866 List<Expression> extendedOperands= parentInfixExpression.extendedOperands(); 2867 Expression sibiling; 2868 if (expression.equals(parentInfixExpression.getLeftOperand())) { 2869 sibiling= parentInfixExpression.getRightOperand(); 2870 } else if (expression.equals(parentInfixExpression.getRightOperand())) { 2871 if (parentInfixExpression.getParent() instanceof InfixExpression) { 2872 sibiling= getNextSiblingExpression(parentInfixExpression); 2873 } else if (extendedOperands.size() > 0) { 2874 sibiling= extendedOperands.get(0); 2875 } else { 2876 sibiling= null; 2877 } 2878 } else if (extendedOperands.contains(expression)) { 2879 sibiling= null; 2880 ListIterator<Expression> it= extendedOperands.listIterator(); 2881 while (it.hasNext()) { 2882 if (expression.equals(it.next()) && it.hasNext()) { 2883 sibiling= it.next(); 2884 break; 2885 } 2886 } 2887 } else { 2888 sibiling= null; 2889 } 2890 return sibiling; 2891 } 2892 createSwitchCaseStatements(AST ast, ASTRewrite rewrite, List<Expression> caseExpressions)2893 private static SwitchCase[] createSwitchCaseStatements(AST ast, ASTRewrite rewrite, List<Expression> caseExpressions) { 2894 int len= (caseExpressions.isEmpty()) ? 1 : caseExpressions.size(); 2895 SwitchCase[] switchCaseStatements= new SwitchCase[len]; 2896 if (caseExpressions.isEmpty()) { 2897 switchCaseStatements[0]= ast.newSwitchCase(); 2898 if (!ASTHelper.isSwitchCaseExpressionsSupportedInAST(ast)) { 2899 switchCaseStatements[0].setExpression(null); 2900 } 2901 } else { 2902 for (int i= 0; i < caseExpressions.size(); i++) { 2903 ASTNode astNode= caseExpressions.get(i); 2904 switchCaseStatements[i]= ast.newSwitchCase(); 2905 Expression copyTarget= (Expression) rewrite.createCopyTarget(astNode); 2906 if (ASTHelper.isSwitchCaseExpressionsSupportedInAST(ast)) { 2907 switchCaseStatements[i].expressions().add(copyTarget); 2908 } else { 2909 switchCaseStatements[i].setExpression(copyTarget); 2910 } 2911 } 2912 } 2913 return switchCaseStatements; 2914 } 2915 2916 } 2917