1414package org .eclipse .jdt .internal .corext .fix ;
1515
1616import java .util .ArrayList ;
17+ import java .util .Collection ;
18+ import java .util .HashSet ;
1719import java .util .List ;
1820import java .util .Objects ;
21+ import java .util .Set ;
1922
2023import org .eclipse .core .runtime .CoreException ;
2124
2225import org .eclipse .text .edits .TextEditGroup ;
2326
27+ import org .eclipse .jdt .core .IJavaProject ;
2428import org .eclipse .jdt .core .dom .AST ;
2529import org .eclipse .jdt .core .dom .ASTNode ;
2630import org .eclipse .jdt .core .dom .ASTVisitor ;
2731import org .eclipse .jdt .core .dom .Assignment ;
2832import org .eclipse .jdt .core .dom .Block ;
2933import org .eclipse .jdt .core .dom .CastExpression ;
3034import org .eclipse .jdt .core .dom .CompilationUnit ;
35+ import org .eclipse .jdt .core .dom .ConditionalExpression ;
3136import org .eclipse .jdt .core .dom .Expression ;
3237import org .eclipse .jdt .core .dom .ExpressionStatement ;
38+ import org .eclipse .jdt .core .dom .ITypeBinding ;
3339import org .eclipse .jdt .core .dom .IfStatement ;
3440import org .eclipse .jdt .core .dom .InfixExpression ;
3541import org .eclipse .jdt .core .dom .InfixExpression .Operator ;
3642import org .eclipse .jdt .core .dom .InstanceofExpression ;
43+ import org .eclipse .jdt .core .dom .MethodDeclaration ;
3744import org .eclipse .jdt .core .dom .Modifier ;
3845import org .eclipse .jdt .core .dom .Modifier .ModifierKeyword ;
46+ import org .eclipse .jdt .core .dom .ParenthesizedExpression ;
3947import org .eclipse .jdt .core .dom .PatternInstanceofExpression ;
4048import org .eclipse .jdt .core .dom .PrefixExpression ;
4149import org .eclipse .jdt .core .dom .SimpleName ;
4856import org .eclipse .jdt .core .dom .rewrite .ASTRewrite ;
4957import org .eclipse .jdt .core .dom .rewrite .TargetSourceRangeComputer ;
5058
59+ import org .eclipse .jdt .internal .core .manipulation .StubUtility ;
5160import org .eclipse .jdt .internal .corext .dom .ASTNodes ;
61+ import org .eclipse .jdt .internal .corext .dom .ScopeAnalyzer ;
5262import org .eclipse .jdt .internal .corext .refactoring .structure .CompilationUnitRewrite ;
5363import org .eclipse .jdt .internal .corext .refactoring .structure .ImportRemover ;
5464
5868
5969public class PatternMatchingForInstanceofFixCore extends CompilationUnitRewriteOperationsFixCore {
6070 public static final class PatternMatchingForInstanceofFinder extends ASTVisitor {
61- private List <PatternMatchingForInstanceofFixOperation > fResult ;
71+ private List <CompilationUnitRewriteOperation > fResult ;
6272
63- public PatternMatchingForInstanceofFinder (List <PatternMatchingForInstanceofFixOperation > ops ) {
73+ public PatternMatchingForInstanceofFinder (List <CompilationUnitRewriteOperation > ops ) {
6474 fResult = ops ;
6575 }
6676
@@ -74,6 +84,7 @@ public boolean visit(final Block visited) {
7484 final class InstanceofVisitor extends ASTVisitor {
7585 private final Block startNode ;
7686 private boolean result = true ;
87+ private final Set <String > excludedNames = new HashSet <>();
7788
7889 public InstanceofVisitor (final Block startNode ) {
7990 this .startNode = startNode ;
@@ -101,9 +112,14 @@ public boolean visit(final InstanceofExpression visited) {
101112 boolean isPositiveCaseToAnalyze = true ;
102113 ASTNode currentNode = visited ;
103114
115+ if (visited .getLocationInParent () == ConditionalExpression .EXPRESSION_PROPERTY ) {
116+
117+ }
104118 while (currentNode .getParent () != null
105119 && (!(currentNode .getParent () instanceof IfStatement )
106- || currentNode .getLocationInParent () != IfStatement .EXPRESSION_PROPERTY )) {
120+ || currentNode .getLocationInParent () != IfStatement .EXPRESSION_PROPERTY )
121+ && (!(currentNode .getParent () instanceof ConditionalExpression )
122+ || currentNode .getLocationInParent () != ConditionalExpression .EXPRESSION_PROPERTY )) {
107123 switch (currentNode .getParent ().getNodeType ()) {
108124 case ASTNode .PARENTHESIZED_EXPRESSION :
109125 break ;
@@ -143,22 +159,96 @@ public boolean visit(final InstanceofExpression visited) {
143159 }
144160 }
145161
146- IfStatement ifStatement = (IfStatement ) currentNode .getParent ();
162+ if (currentNode .getParent () instanceof ConditionalExpression condExp
163+ && visited .getLeftOperand () instanceof SimpleName name ) {
164+ ConditionalCollector collector = new ConditionalCollector (visited , name , excludedNames );
165+ if (isPositiveCaseToAnalyze ) {
166+ condExp .getThenExpression ().accept (collector );
167+ } else {
168+ condExp .getElseExpression ().accept (collector );
169+ }
170+ if (collector .hasResult ()) {
171+ fResult .add (collector .build ());
172+ return false ;
173+ }
174+ } else {
175+ IfStatement ifStatement = (IfStatement ) currentNode .getParent ();
176+
177+ ResultCollector collector = new ResultCollector (visited );
178+ if (isPositiveCaseToAnalyze ) {
179+ collector .collect (ifStatement .getThenStatement ());
180+ } else if (ifStatement .getElseStatement () != null ) {
181+ collector .collect (ifStatement .getElseStatement ());
182+ } else if (ASTNodes .fallsThrough (ifStatement .getThenStatement ())) {
183+ collector .collect (ASTNodes .getNextSibling (ifStatement ));
184+ }
185+
186+ if (collector .hasResult ()) {
187+ fResult .add (collector .build ());
188+ return false ;
189+ }
190+ }
191+ return true ;
192+ }
193+
194+ static class ConditionalCollector extends ASTVisitor {
195+ final InstanceofExpression visited ;
196+ final Set <String > excludedNames ;
197+ final SimpleName variableName ;
198+ String replacementName = null ;
199+ final List <Expression > expressionsToReplace = new ArrayList <>();
200+
201+ ConditionalCollector (InstanceofExpression visited , SimpleName name , Set <String > excludedNames ) {
202+ this .visited = visited ;
203+ this .excludedNames = excludedNames ;
204+ this .variableName = name ; }
147205
148- ResultCollector collector = new ResultCollector (visited );
149- if (isPositiveCaseToAnalyze ) {
150- collector .collect (ifStatement .getThenStatement ());
151- } else if (ifStatement .getElseStatement () != null ) {
152- collector .collect (ifStatement .getElseStatement ());
153- } else if (ASTNodes .fallsThrough (ifStatement .getThenStatement ())) {
154- collector .collect (ASTNodes .getNextSibling (ifStatement ));
206+ public PatternMatchingForConditionalInstanceofFixOperation build () {
207+ return new PatternMatchingForConditionalInstanceofFixOperation (visited , expressionsToReplace , replacementName );
155208 }
156209
157- if (collector .hasResult ()) {
158- fResult .add (collector .build ());
210+ boolean hasResult () {
211+ return replacementName != null && !expressionsToReplace .isEmpty ();
212+ }
213+
214+ void addMatching (final CastExpression castExp , final SimpleName name ) {
215+ Expression expressionToReplace = castExp ;
216+ ITypeBinding castBinding = castExp .resolveTypeBinding ();
217+ if (this .variableName .getIdentifier ().equals (name .getIdentifier ())
218+ && castBinding != null && castBinding .isEqualTo (visited .getRightOperand ().resolveBinding ())) {
219+ while (expressionToReplace .getLocationInParent () == ParenthesizedExpression .EXPRESSION_PROPERTY ) {
220+ expressionToReplace = (Expression ) expressionToReplace .getParent ();
221+ }
222+ if (replacementName == null ) {
223+ String baseName = castBinding .getName ().toLowerCase ().substring (0 , 1 );
224+ MethodDeclaration methodDecl = ASTNodes .getFirstAncestorOrNull (name , MethodDeclaration .class );
225+ if (methodDecl != null ) {
226+ CompilationUnit cu = (CompilationUnit ) name .getRoot ();
227+ IJavaProject project = cu .getJavaElement ().getJavaProject ();
228+ replacementName = proposeLocalName (baseName , cu , project , methodDecl , excludedNames .toArray (new String [0 ]));
229+ }
230+ }
231+ this .expressionsToReplace .add (expressionToReplace );
232+ }
233+ }
234+
235+ private String proposeLocalName (String baseName , CompilationUnit root , IJavaProject javaProject , MethodDeclaration methodDecl , String [] usedNames ) {
236+ // don't propose names that are already in use:
237+ Collection <String > variableNames = new ScopeAnalyzer (root ).getUsedVariableNames (methodDecl .getStartPosition (), methodDecl .getLength ());
238+ String [] names = new String [variableNames .size () + usedNames .length ];
239+ variableNames .toArray (names );
240+ System .arraycopy (usedNames , 0 , names , variableNames .size (), usedNames .length );
241+ return StubUtility .getLocalNameSuggestions (javaProject , baseName , 0 , names )[0 ];
242+ }
243+
244+ @ Override
245+ public boolean visit (SimpleName node ) {
246+ if (node .getLocationInParent () == CastExpression .EXPRESSION_PROPERTY ) {
247+ addMatching ((CastExpression )node .getParent (), node );
248+ }
159249 return false ;
160250 }
161- return true ;
251+
162252 }
163253
164254 static class ResultCollector {
@@ -256,6 +346,46 @@ boolean collect(final Statement conditionalStatements) {
256346 }
257347 }
258348
349+ public static class PatternMatchingForConditionalInstanceofFixOperation extends CompilationUnitRewriteOperation {
350+ private final InstanceofExpression nodeToComplete ;
351+ private final List <Expression > expressionsToReplace ;
352+ private final String replacementName ;
353+
354+ public PatternMatchingForConditionalInstanceofFixOperation (final InstanceofExpression nodeToComplete , final List <Expression > expressionsToReplace , final String replacementName ) {
355+ this .nodeToComplete = nodeToComplete ;
356+ this .expressionsToReplace = expressionsToReplace ;
357+ this .replacementName = replacementName ;
358+ }
359+
360+ @ SuppressWarnings ("deprecation" )
361+ @ Override
362+ public void rewriteAST (final CompilationUnitRewrite cuRewrite , final LinkedProposalModelCore linkedModel ) throws CoreException {
363+ ASTRewrite rewrite = cuRewrite .getASTRewrite ();
364+ AST ast = rewrite .getAST ();
365+ TextEditGroup group = createTextEditGroup (MultiFixMessages .PatternMatchingForInstanceofCleanup_description , cuRewrite );
366+
367+ PatternInstanceofExpression newInstanceof = ast .newPatternInstanceofExpression ();
368+ newInstanceof .setLeftOperand (ASTNodes .createMoveTarget (rewrite , nodeToComplete .getLeftOperand ()));
369+ SingleVariableDeclaration newSVDecl = ast .newSingleVariableDeclaration ();
370+ newSVDecl .setName (ast .newSimpleName (replacementName ));
371+ newSVDecl .setType (ASTNodes .createMoveTarget (rewrite , nodeToComplete .getRightOperand ()));
372+ if ((ast .apiLevel () == AST .JLS20 && ast .isPreviewEnabled ()) || ast .apiLevel () > AST .JLS20 ) {
373+ TypePattern newTypePattern = ast .newTypePattern ();
374+ newTypePattern .setPatternVariable ((VariableDeclaration ) newSVDecl );
375+ newInstanceof .setPattern (newTypePattern );
376+ } else {
377+ newInstanceof .setRightOperand (newSVDecl );
378+ }
379+ ASTNodes .replaceButKeepComment (rewrite , nodeToComplete , newInstanceof , group );
380+
381+ ASTNodes .replaceButKeepComment (rewrite , nodeToComplete , newInstanceof , group );
382+ for (Expression expressionToReplace : expressionsToReplace ) {
383+ SimpleName name = ast .newSimpleName (replacementName );
384+ rewrite .replace (expressionToReplace , name , group );
385+ }
386+ }
387+ }
388+
259389 public static class PatternMatchingForInstanceofFixOperation extends CompilationUnitRewriteOperation {
260390 private final InstanceofExpression nodeToComplete ;
261391 private final List <VariableDeclarationStatement > statementsToRemove ;
@@ -325,7 +455,7 @@ public SourceRange computeSourceRange(final ASTNode nodeWithComment) {
325455
326456
327457 public static ICleanUpFix createCleanUp (final CompilationUnit compilationUnit ) {
328- List <PatternMatchingForInstanceofFixOperation > operations = new ArrayList <>();
458+ List <CompilationUnitRewriteOperation > operations = new ArrayList <>();
329459 PatternMatchingForInstanceofFinder finder = new PatternMatchingForInstanceofFinder (operations );
330460 compilationUnit .accept (finder );
331461
0 commit comments