This is an automated email from the ASF dual-hosted git repository.

lkishalmi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-netbeans.git


The following commit(s) were added to refs/heads/master by this push:
     new a91ade4  [NETBEANS-1675]Java Hint to fix error :different case kinds 
used in s… (#1126)
a91ade4 is described below

commit a91ade4963d0322b575402375b3a169c1eabc11b
Author: Vikas Prabhakar <vikas.prabha...@oracle.com>
AuthorDate: Wed Mar 6 20:26:33 2019 +0530

    [NETBEANS-1675]Java Hint to fix error :different case kinds used in s… 
(#1126)
    
    * [NETBEANS-1675]Java Hint to fix error :different case kinds used in 
switch expressions
    
    * Use getBody method
    
    * Add review comment changes
    
    * Change hint message
---
 .../modules/java/hints/errors/Bundle.properties    |   2 +
 .../java/hints/errors/DifferentCaseKindsFix.java   | 143 ++++++++
 .../modules/java/hints/errors/Utilities.java       | 195 +++++++++-
 .../java/hints/jdk/ConvertSwitchToRuleSwitch.java  | 185 +---------
 .../modules/java/hints/resources/layer.xml         |   1 +
 .../java/hints/errors/Bundle_test.properties       |   1 +
 .../hints/errors/DifferentCaseKindsFixTest.java    | 397 +++++++++++++++++++++
 java/java.source.base/apichanges.xml               |  12 +
 java/java.source.base/nbproject/project.properties |   2 +-
 .../org/netbeans/api/java/source/TreeMaker.java    |  14 +-
 .../org/netbeans/api/java/source/WorkingCopy.java  |   6 +
 .../netbeans/modules/java/source/TreeShims.java    |  15 +
 .../modules/java/source/builder/TreeFactory.java   |   7 +-
 .../modules/java/source/matching/CopyFinder.java   |  11 +-
 .../modules/java/source/pretty/VeryPretty.java     |  19 +-
 .../modules/java/source/save/CasualDiff.java       |  31 ++
 .../source/transform/ImmutableTreeTranslator.java  |  13 +
 .../api/java/source/gen/SwitchExpressionTest.java  | 215 +++++++++++
 18 files changed, 1073 insertions(+), 196 deletions(-)

diff --git 
a/java/java.hints/src/org/netbeans/modules/java/hints/errors/Bundle.properties 
b/java/java.hints/src/org/netbeans/modules/java/hints/errors/Bundle.properties
index 4b69f81..6f8a035 100644
--- 
a/java/java.hints/src/org/netbeans/modules/java/hints/errors/Bundle.properties
+++ 
b/java/java.hints/src/org/netbeans/modules/java/hints/errors/Bundle.properties
@@ -197,3 +197,5 @@ FIX_AccessError_PROTECTED=Make {0} protected
 FIX_AccessError_PACKAGE_PRIVATE=Make {0} package private
 ImportClassCustomizer.organizeImports.text=Format and sort imports
 FIX_VarCompDeclaration=Split compound declaration
+FIX_DifferentCaseKinds=Convert to rule switch case
+
diff --git 
a/java/java.hints/src/org/netbeans/modules/java/hints/errors/DifferentCaseKindsFix.java
 
b/java/java.hints/src/org/netbeans/modules/java/hints/errors/DifferentCaseKindsFix.java
new file mode 100644
index 0000000..c91f65e1
--- /dev/null
+++ 
b/java/java.hints/src/org/netbeans/modules/java/hints/errors/DifferentCaseKindsFix.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.hints.errors;
+
+import com.sun.source.tree.CaseTree;
+import com.sun.source.tree.SwitchTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.netbeans.api.java.queries.CompilerOptionsQuery;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.modules.java.hints.spi.ErrorRule;
+import org.netbeans.modules.java.source.TreeShims;
+import org.netbeans.spi.editor.hints.Fix;
+import org.netbeans.spi.java.hints.JavaFix;
+import org.netbeans.spi.java.hints.JavaFix.TransformationContext;
+import org.openide.util.NbBundle;
+
+/**
+ * Handle error rule "compiler.err.switch.mixing.case.types" and provide the
+ * fix.
+ *
+ * @author vkprabha
+ */
+public class DifferentCaseKindsFix implements ErrorRule<Void> {
+
+    private static final Set<String> ERROR_CODES = new 
HashSet<String>(Arrays.asList(
+            "compiler.err.switch.mixing.case.types")); // NOI18N
+    
+    @Override
+    public Set<String> getCodes() {
+        return Collections.unmodifiableSet(ERROR_CODES);
+    }
+
+    @Override
+    public List<Fix> run(CompilationInfo info, String diagnosticKey, int 
offset, TreePath treePath, Data<Void> data) {
+        if 
(!CompilerOptionsQuery.getOptions(info.getFileObject()).getArguments().contains("--enable-preview"))
 {
+            return null;
+        }
+        TreePath parentPath = treePath.getParentPath();
+        List<? extends CaseTree> caseTrees = null;
+        boolean flag = false;
+        
if(parentPath.getLeaf().getKind().toString().equals("SWITCH_EXPRESSION")){
+            caseTrees = TreeShims.getCases(parentPath.getLeaf());            
+        } else {
+            flag = true;
+            caseTrees = ((SwitchTree) 
treePath.getParentPath().getLeaf()).getCases();
+        }
+            boolean completesNormally = false;
+            boolean wasDefault = false;
+            boolean wasEmpty = false;
+            for (CaseTree ct : caseTrees) {
+                if (ct.getStatements() == null && TreeShims.getBody(ct) == 
null) {
+                    return null;
+                } else if (flag && ct.getStatements() != null) {
+                    if (completesNormally) {
+                        if (!wasEmpty) {//fall-through from a non-empty case
+                            return null;
+                        }
+                        if (wasDefault) {//fall-through from default to a case
+                            return null;
+                        }
+                        if (!wasDefault && ct.getExpression() == null) 
{//fall-through from a case to default
+                            return null;
+                        }
+                    }
+                    completesNormally = Utilities.completesNormally(info, new 
TreePath(treePath.getParentPath(), ct));
+                    wasDefault = ct.getExpression() == null;
+                    wasEmpty = ct.getStatements().isEmpty();
+                }
+            }      
+
+        return Collections.<Fix>singletonList(new 
DifferentCaseKindsFix.FixImpl(info, treePath).toEditorFix());
+    }    
+
+    @Override
+    public String getId() {
+        return DifferentCaseKindsFix.class.getName();
+    }
+
+    @Override
+    public String getDisplayName() {
+        return NbBundle.getMessage(DifferentCaseKindsFix.class, 
"FIX_DifferentCaseKinds"); // NOI18N
+    }
+
+    public String getDescription() {
+        return NbBundle.getMessage(DifferentCaseKindsFix.class, 
"FIX_DifferentCaseKinds"); // NOI18N
+    }
+
+    @Override
+    public void cancel() {
+    }
+
+    private static final class FixImpl extends JavaFix {
+
+        CompilationInfo info;
+        TreePath path;
+
+        public FixImpl(CompilationInfo info, TreePath path) {
+            super(info, path);
+            this.info = info;
+            this.path = path;
+        }
+
+        @Override
+        protected String getText() {
+            return NbBundle.getMessage(DifferentCaseKindsFix.class, 
"FIX_DifferentCaseKinds"); // NOI18N
+        }
+
+        public String toDebugString() {
+            return NbBundle.getMessage(DifferentCaseKindsFix.class, 
"FIX_DifferentCaseKinds"); // NOI18N
+        }
+
+        @Override
+        protected void performRewrite(TransformationContext ctx) {
+            TreePath tp = ctx.getPath();
+            Tree switchBlock = tp.getParentPath().getLeaf();
+            Utilities.performRewriteRuleSwitch(ctx, tp, switchBlock);
+        }
+
+    } 
+    
+}
diff --git 
a/java/java.hints/src/org/netbeans/modules/java/hints/errors/Utilities.java 
b/java/java.hints/src/org/netbeans/modules/java/hints/errors/Utilities.java
index fbb6a15..43886a2 100644
--- a/java/java.hints/src/org/netbeans/modules/java/hints/errors/Utilities.java
+++ b/java/java.hints/src/org/netbeans/modules/java/hints/errors/Utilities.java
@@ -125,10 +125,10 @@ import org.openide.util.Exceptions;
 
 import static com.sun.source.tree.Tree.Kind.*;
 import com.sun.source.tree.UnaryTree;
+import com.sun.source.util.TreePathScanner;
 import com.sun.tools.javac.api.JavacScope;
 import com.sun.tools.javac.api.JavacTaskImpl;
 import com.sun.tools.javac.code.Type;
-import com.sun.tools.javac.comp.ArgumentAttr;
 import com.sun.tools.javac.comp.Attr;
 import com.sun.tools.javac.comp.AttrContext;
 import com.sun.tools.javac.comp.Enter;
@@ -149,6 +149,8 @@ import org.netbeans.api.java.source.CodeStyle;
 import org.netbeans.api.java.source.CodeStyleUtils;
 import org.netbeans.api.java.source.TreeMaker;
 import org.netbeans.modules.java.source.JavaSourceAccessor;
+import org.netbeans.modules.java.source.TreeShims;
+import org.netbeans.spi.java.hints.JavaFix;
 import org.netbeans.spi.java.hints.JavaFixUtilities;
 import org.openide.util.Pair;
 
@@ -3033,4 +3035,195 @@ public class Utilities {
         return elementToReturn;
     }
 
+    public static boolean completesNormally(CompilationInfo info, TreePath tp) 
{
+        class Scanner extends TreePathScanner<Void, Void> {
+
+            private boolean completesNormally = true;
+            private Set<Tree> seenTrees = new HashSet<>();
+
+            @Override
+            public Void visitReturn(ReturnTree node, Void p) {
+                completesNormally = false;
+                return null;
+            }
+
+            @Override
+            public Void visitBreak(BreakTree node, Void p) {
+                completesNormally &= 
seenTrees.contains(info.getTreeUtilities().getBreakContinueTarget(getCurrentPath()));
+                return null;
+            }
+
+            @Override
+            public Void visitContinue(ContinueTree node, Void p) {
+                completesNormally &= 
seenTrees.contains(info.getTreeUtilities().getBreakContinueTarget(getCurrentPath()));
+                return null;
+            }
+
+            @Override
+            public Void visitThrow(ThrowTree node, Void p) {
+                completesNormally = false;
+                return null;
+            }
+
+            @Override
+            public Void visitIf(IfTree node, Void p) {
+                boolean origCompletesNormally = completesNormally;
+                scan(node.getThenStatement(), p);
+                boolean afterThen = completesNormally;
+                completesNormally = origCompletesNormally;
+                scan(node.getElseStatement(), p);
+                completesNormally |= afterThen;
+                return null;
+            }
+
+            @Override
+            public Void visitSwitch(SwitchTree node, Void p) {
+                //exhaustiveness: (TODO)
+                boolean hasDefault = node.getCases().stream().anyMatch(c -> 
c.getExpression() == null);
+                if (node.getCases().size() > 0) {
+                    scan(node.getCases().get(node.getCases().size() - 1), p);
+                }
+                completesNormally |= !hasDefault;
+                return null;
+            }
+
+            //TODO: loops
+            @Override
+            public Void scan(Tree tree, Void p) {
+                seenTrees.add(tree);
+                return super.scan(tree, p);
+            }
+
+            @Override
+            public Void visitLambdaExpression(LambdaExpressionTree node, Void 
p) {
+                return null;
+            }
+
+            @Override
+            public Void visitClass(ClassTree node, Void p) {
+                return null;
+            }
+        }
+
+        Scanner scanner = new Scanner();
+
+        scanner.scan(tp, null);
+        return scanner.completesNormally;
+    }
+
+    public static void performRewriteRuleSwitch(JavaFix.TransformationContext 
ctx, TreePath tp, Tree st) {
+        WorkingCopy wc = ctx.getWorkingCopy();
+        TreeMaker make = wc.getTreeMaker();
+        List<CaseTree> newCases = new ArrayList<>();
+        List<? extends CaseTree> cases;
+        Set<VariableElement> variablesDeclaredInOtherCases = new HashSet<>();
+        List<ExpressionTree> patterns = new ArrayList<>();
+        boolean switchExpressionFlag = 
st.getKind().toString().equals("SWITCH_EXPRESSION");
+        if (switchExpressionFlag) {
+            cases = TreeShims.getCases(st);
+        } else {
+            cases = ((SwitchTree) st).getCases();
+        }
+        for (Iterator<? extends CaseTree> it = cases.iterator(); 
it.hasNext();) {
+            CaseTree ct = it.next();
+            TreePath casePath = new TreePath(tp, ct);
+            patterns.addAll(TreeShims.getExpressions(ct));
+            List<StatementTree> statements;
+            if (ct.getStatements() == null) {
+                statements = new ArrayList<>(((JCTree.JCCase) 
ct).stats);//Collections.singletonList((StatementTree) TreeShims.getBody(ct));
+            } else {
+                statements = new ArrayList<>(ct.getStatements());
+            }
+            if (statements.isEmpty()) {
+                if (it.hasNext()) {
+                    continue;
+                }
+                //last case, no break
+            } else if (!switchExpressionFlag && 
statements.get(statements.size() - 1).getKind() == Tree.Kind.BREAK
+                    && 
ctx.getWorkingCopy().getTreeUtilities().getBreakContinueTarget(new TreePath(new 
TreePath(tp, ct), statements.get(statements.size() - 1))) == st) {
+                statements.remove(statements.size() - 1);
+            } else {
+                new TreePathScanner<Void, Void>() {
+                    @Override
+                    public Void visitBlock(BlockTree node, Void p) {
+                        if (!node.getStatements().isEmpty()
+                                && 
node.getStatements().get(node.getStatements().size() - 1).getKind() == 
Tree.Kind.BREAK
+                                && 
ctx.getWorkingCopy().getTreeUtilities().getBreakContinueTarget(new 
TreePath(getCurrentPath(), node.getStatements().get(node.getStatements().size() 
- 1))) == st) {
+                            wc.rewrite(node, make.removeBlockStatement(node, 
node.getStatements().get(node.getStatements().size() - 1)));
+                            //TODO: optimize ifs?
+                        }
+                        return super.visitBlock(node, p);
+                    }
+                }.scan(new TreePath(new TreePath(tp, ct), 
statements.get(statements.size() - 1)), null);
+            }
+            Set<Element> seenVariables = new HashSet<>();
+            int idx = 0;
+            for (StatementTree statement : new ArrayList<>(statements)) {
+                TreePath statementPath = new TreePath(casePath, statement);
+                if (statement.getKind() == Tree.Kind.EXPRESSION_STATEMENT) {
+                    ExpressionTree expr = ((ExpressionStatementTree) 
statement).getExpression();
+                    if (expr.getKind() == Tree.Kind.ASSIGNMENT) {
+                        AssignmentTree at = (AssignmentTree) expr;
+                        Element var = wc.getTrees().getElement(new 
TreePath(new TreePath(statementPath, at), at.getVariable()));
+                        if (variablesDeclaredInOtherCases.contains(var)) {
+                            seenVariables.add(var);
+                            //XXX: take type from the original variable
+                            wc.rewrite(statement,
+                                    
make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), 
var.getSimpleName(), make.Type(var.asType()), at.getExpression()));
+                        }
+                    }
+                }
+                Set<Element> thisStatementSeenVariables = new HashSet<>();
+                new TreePathScanner<Void, Void>() {
+                    @Override
+                    public Void visitIdentifier(IdentifierTree node, Void p) {
+                        Element el = 
wc.getTrees().getElement(getCurrentPath());
+                        if (variablesDeclaredInOtherCases.contains(el) && 
seenVariables.add(el)) {
+                            thisStatementSeenVariables.add(el);
+                        }
+                        return super.visitIdentifier(node, p);
+                    }
+                }.scan(statementPath, null);
+
+                if (!thisStatementSeenVariables.isEmpty()) {
+                    for (Element el : thisStatementSeenVariables) {
+                        VariableElement var = (VariableElement) el;
+                        statements.add(idx++, 
make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), 
var.getSimpleName(), make.Type(var.asType()), null));
+                    }
+                }
+                idx++;
+            }
+            Tree body = make.Block(statements, false);
+            if (statements.size() == 1) {
+                if (statements.get(0).getKind() == 
Tree.Kind.EXPRESSION_STATEMENT
+                        || statements.get(0).getKind() == Tree.Kind.THROW
+                        || statements.get(0).getKind() == Tree.Kind.BLOCK) {
+                    body = statements.get(0);
+                }
+            }
+            newCases.add(make.Case(patterns, body));
+            patterns = new ArrayList<>();
+            for (StatementTree statement : getSwitchStatement(ct)) {
+                if (statement.getKind() == Tree.Kind.VARIABLE) {
+                    variablesDeclaredInOtherCases.add((VariableElement) 
wc.getTrees().getElement(new TreePath(casePath, statement)));
+                }
+            }
+        }
+        if (switchExpressionFlag) {
+            wc.rewrite(st, 
make.SwitchExpression(TreeShims.getExpressions(st).get(0), newCases));
+        } else {
+            wc.rewrite((SwitchTree) st, make.Switch(((SwitchTree) 
st).getExpression(), newCases));
+        }
+    }
+
+    private static List<? extends StatementTree> getSwitchStatement(CaseTree 
ct) {
+        if (ct.getStatements() != null) {
+            return ct.getStatements();
+        } else if (ct instanceof JCTree.JCCase) {
+            return ((JCTree.JCCase) ct).stats;
+        } else {
+            return null;
+        }
+   }
+
 }
diff --git 
a/java/java.hints/src/org/netbeans/modules/java/hints/jdk/ConvertSwitchToRuleSwitch.java
 
b/java/java.hints/src/org/netbeans/modules/java/hints/jdk/ConvertSwitchToRuleSwitch.java
index 4f8955d..c5b5302 100644
--- 
a/java/java.hints/src/org/netbeans/modules/java/hints/jdk/ConvertSwitchToRuleSwitch.java
+++ 
b/java/java.hints/src/org/netbeans/modules/java/hints/jdk/ConvertSwitchToRuleSwitch.java
@@ -18,40 +18,13 @@
  */
 package org.netbeans.modules.java.hints.jdk;
 
-import com.sun.source.tree.AssignmentTree;
-import com.sun.source.tree.BlockTree;
-import com.sun.source.tree.BreakTree;
 import com.sun.source.tree.CaseTree;
-import com.sun.source.tree.ClassTree;
-import com.sun.source.tree.ContinueTree;
-import com.sun.source.tree.ExpressionStatementTree;
-import com.sun.source.tree.ExpressionTree;
-import com.sun.source.tree.IdentifierTree;
-import com.sun.source.tree.IfTree;
-import com.sun.source.tree.LambdaExpressionTree;
-import com.sun.source.tree.ReturnTree;
-import com.sun.source.tree.StatementTree;
 import com.sun.source.tree.SwitchTree;
-import com.sun.source.tree.ThrowTree;
 import com.sun.source.tree.Tree;
-import com.sun.source.tree.Tree.Kind;
 import com.sun.source.util.TreePath;
-import com.sun.source.util.TreePathScanner;
-import com.sun.source.util.TreeScanner;
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.VariableElement;
 import org.netbeans.api.java.queries.CompilerOptionsQuery;
 import org.netbeans.api.java.source.CompilationInfo;
-import org.netbeans.api.java.source.TreeMaker;
-import org.netbeans.api.java.source.WorkingCopy;
-import org.netbeans.modules.java.hints.TreeShims;
+import org.netbeans.modules.java.hints.errors.Utilities;
 import org.netbeans.spi.editor.hints.ErrorDescription;
 import org.netbeans.spi.java.hints.ErrorDescriptionFactory;
 import org.netbeans.spi.java.hints.Hint;
@@ -89,77 +62,12 @@ public class ConvertSwitchToRuleSwitch {
                 if (!wasDefault && ct.getExpression() == null) //fall-through 
from a case to default
                     return null;
             }
-            completesNormally = completesNormally(ctx.getInfo(), new 
TreePath(ctx.getPath(), ct));
+            completesNormally = Utilities.completesNormally(ctx.getInfo(), new 
TreePath(ctx.getPath(), ct));
             wasDefault = ct.getExpression() == null;
             wasEmpty = ct.getStatements().isEmpty();
         }
         return ErrorDescriptionFactory.forName(ctx, ctx.getPath(), 
Bundle.ERR_ConverSwitchToRuleSwitch(), new FixImpl(ctx.getInfo(), 
ctx.getPath()).toEditorFix());
     }
-    private static boolean completesNormally(CompilationInfo info, TreePath 
tp) {
-        class Scanner extends TreePathScanner<Void, Void> {
-            private boolean completesNormally = true;
-            private Set<Tree> seenTrees = new HashSet<>();
-            @Override
-            public Void visitReturn(ReturnTree node, Void p) {
-                completesNormally = false;
-                return null;
-            }
-            @Override
-            public Void visitBreak(BreakTree node, Void p) {
-                completesNormally &= 
seenTrees.contains(info.getTreeUtilities().getBreakContinueTarget(getCurrentPath()));
-                return null;
-            }
-            @Override
-            public Void visitContinue(ContinueTree node, Void p) {
-                completesNormally &= 
seenTrees.contains(info.getTreeUtilities().getBreakContinueTarget(getCurrentPath()));
-                return null;
-            }
-            @Override
-            public Void visitThrow(ThrowTree node, Void p) {
-                completesNormally = false;
-                return null;
-            }
-            @Override
-            public Void visitIf(IfTree node, Void p) {
-                boolean origCompletesNormally = completesNormally;
-                scan(node.getThenStatement(), p);
-                boolean afterThen = completesNormally;
-                completesNormally = origCompletesNormally;
-                scan(node.getElseStatement(), p);
-                completesNormally |= afterThen;
-                return null;
-            }
-            @Override
-            public Void visitSwitch(SwitchTree node, Void p) {
-                //exhaustiveness: (TODO)
-                boolean hasDefault = node.getCases().stream().anyMatch(c -> 
c.getExpression() == null);
-                if (node.getCases().size() > 0) {
-                    scan(node.getCases().get(node.getCases().size() - 1), p);
-                }
-                completesNormally |= !hasDefault;
-                return null;
-            }
-            //TODO: loops
-            @Override
-            public Void scan(Tree tree, Void p) {
-                seenTrees.add(tree);
-                return super.scan(tree, p);
-            }
-            @Override
-            public Void visitLambdaExpression(LambdaExpressionTree node, Void 
p) {
-                return null;
-            }
-            @Override
-            public Void visitClass(ClassTree node, Void p) {
-                return null;
-            }
-        }
-
-        Scanner scanner = new Scanner();
-
-        scanner.scan(tp, null);
-        return scanner.completesNormally;
-    }
 
     private static final class FixImpl extends JavaFix {
 
@@ -175,96 +83,9 @@ public class ConvertSwitchToRuleSwitch {
 
         @Override
         protected void performRewrite(TransformationContext ctx) {
-            WorkingCopy wc = ctx.getWorkingCopy();
             TreePath tp = ctx.getPath();
-            TreeMaker make = wc.getTreeMaker();
             SwitchTree st = (SwitchTree) tp.getLeaf();
-            List<CaseTree> newCases = new ArrayList<>();
-            List<ExpressionTree> patterns = new ArrayList<>();
-            Set<VariableElement> variablesDeclaredInOtherCases = new 
HashSet<>();
-
-            for (Iterator<? extends CaseTree> it = st.getCases().iterator(); 
it.hasNext();) {
-                CaseTree ct = it.next();
-                TreePath casePath = new TreePath(tp, ct);
-                patterns.addAll(TreeShims.getExpressions(ct));
-                List<StatementTree> statements = new 
ArrayList<>(ct.getStatements());
-                if (statements.isEmpty()) {
-                    if (it.hasNext()) {
-                        continue;
-                    }
-                    //last case, no break
-                } else if (statements.get(statements.size() - 1).getKind() == 
Kind.BREAK &&
-                    
ctx.getWorkingCopy().getTreeUtilities().getBreakContinueTarget(new TreePath(new 
TreePath(tp, ct), statements.get(statements.size() - 1))) == st) {
-                    statements.remove(statements.size() - 1);
-                } else {
-                    new TreePathScanner<Void, Void>() {
-                        @Override
-                        public Void visitBlock(BlockTree node, Void p) {
-                            if (!node.getStatements().isEmpty() &&
-                                    
node.getStatements().get(node.getStatements().size() - 1).getKind() == 
Kind.BREAK &&
-                                    
ctx.getWorkingCopy().getTreeUtilities().getBreakContinueTarget(new 
TreePath(getCurrentPath(), node.getStatements().get(node.getStatements().size() 
- 1))) == st) {
-                                wc.rewrite(node, 
make.removeBlockStatement(node, 
node.getStatements().get(node.getStatements().size() - 1)));
-                                //TODO: optimize ifs?
-                            }
-                            return super.visitBlock(node, p);
-                        }
-                    }.scan(new TreePath(new TreePath(tp, ct), 
statements.get(statements.size() - 1)), null);
-                }
-                Set<Element> seenVariables = new HashSet<>();
-                int idx = 0;
-                for (StatementTree statement : new ArrayList<>(statements)) {
-                    TreePath statementPath = new TreePath(casePath, statement);
-                    if (statement.getKind() == Kind.EXPRESSION_STATEMENT) {
-                        ExpressionTree expr = ((ExpressionStatementTree) 
statement).getExpression();
-                        if (expr.getKind() == Kind.ASSIGNMENT) {
-                            AssignmentTree at = (AssignmentTree) expr;
-                            Element var = wc.getTrees().getElement(new 
TreePath(new TreePath(statementPath, at), at.getVariable()));
-                            if (variablesDeclaredInOtherCases.contains(var)) {
-                                seenVariables.add(var);
-                                //XXX: take type from the original variable
-                                wc.rewrite(statement,
-                                           
make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), 
var.getSimpleName(), make.Type(var.asType()), at.getExpression()));
-                            }
-                        }
-                    }
-                    Set<Element> thisStatementSeenVariables = new HashSet<>();
-                    new TreePathScanner<Void, Void>() {
-                        @Override
-                        public Void visitIdentifier(IdentifierTree node, Void 
p) {
-                            Element el = 
wc.getTrees().getElement(getCurrentPath());
-                            if (variablesDeclaredInOtherCases.contains(el) && 
seenVariables.add(el)) {
-                                thisStatementSeenVariables.add(el);
-                            }
-                            return super.visitIdentifier(node, p);
-                        }
-                    }.scan(statementPath, null);
-
-                    if (!thisStatementSeenVariables.isEmpty()) {
-                        for (Element el : thisStatementSeenVariables) {
-                            VariableElement var = (VariableElement) el;
-                            statements.add(idx++, 
make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), 
var.getSimpleName(), make.Type(var.asType()), null));
-                        }
-                    }
-                    idx++;
-                }
-                Tree body = make.Block(statements, false);
-                if (statements.size() == 1) {
-                    if (statements.get(0).getKind() == 
Kind.EXPRESSION_STATEMENT ||
-                            statements.get(0).getKind() == Kind.THROW ||
-                            statements.get(0).getKind() == Kind.BLOCK) {
-                        body = statements.get(0);
-                    }
-                }
-                newCases.add(make.Case(patterns, body));
-                patterns = new ArrayList<>();
-                for (StatementTree statement : ct.getStatements()) {
-                    if (statement.getKind() == Kind.VARIABLE) {
-                        variablesDeclaredInOtherCases.add((VariableElement) 
wc.getTrees().getElement(new TreePath(casePath, statement)));
-                    }
-                }
-            }
-
-            wc.rewrite(st, make.Switch(st.getExpression(), newCases));
+            Utilities.performRewriteRuleSwitch(ctx, tp, st);
         }
         
     }
diff --git 
a/java/java.hints/src/org/netbeans/modules/java/hints/resources/layer.xml 
b/java/java.hints/src/org/netbeans/modules/java/hints/resources/layer.xml
index aa9d4e9..96786f7 100644
--- a/java/java.hints/src/org/netbeans/modules/java/hints/resources/layer.xml
+++ b/java/java.hints/src/org/netbeans/modules/java/hints/resources/layer.xml
@@ -148,6 +148,7 @@
                 <file 
name="org-netbeans-modules-java-hints-errors-ImportClass.instance"/>
                 <file 
name="org-netbeans-modules-java-hints-errors-AddCast.instance"/>
                 <file 
name="org-netbeans-modules-java-hints-errors-VarCompDeclaration.instance"/>
+                <file 
name="org-netbeans-modules-java-hints-errors-DifferentCaseKindsFix.instance"/>
                 <file 
name="org-netbeans-modules-java-hints-errors-CreateElement.instance"/>
                 <file 
name="org-netbeans-modules-java-hints-errors-ChangeMethodParameters.instance"/>
                 <file 
name="org-netbeans-modules-java-hints-errors-RenameConstructor.instance"/>
diff --git 
a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/Bundle_test.properties
 
b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/Bundle_test.properties
index 334ec9c..c0a0acc 100644
--- 
a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/Bundle_test.properties
+++ 
b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/Bundle_test.properties
@@ -52,6 +52,7 @@ FIX_AccessError_PUBLIC=FIX_AccessError_PUBLIC:{0}
 FIX_AccessError_PROTECTED=FIX_AccessError_PROTECTED:{0}
 FIX_AccessError_PACKAGE_PRIVATE=FIX_AccessError_PACKAGE_PRIVATE:{0}
 FIX_VarCompDeclaration=FIX_VarCompDeclaration
+FIX_DifferentCaseKinds=FIX_DifferentCaseKinds
 
 LBL_Impl_Abstract_Methods=LBL_Impl_Abstract_Methods
 ERR_CannotOverrideAbstractMethods=ERR_CannotOverrideAbstractMethods
diff --git 
a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/DifferentCaseKindsFixTest.java
 
b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/DifferentCaseKindsFixTest.java
new file mode 100644
index 0000000..9193cfb
--- /dev/null
+++ 
b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/DifferentCaseKindsFixTest.java
@@ -0,0 +1,397 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.hints.errors;
+
+import com.sun.source.util.TreePath;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import javax.swing.event.ChangeListener;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.modules.java.hints.infrastructure.ErrorHintsTestBase;
+import org.netbeans.modules.java.source.parsing.JavacParser;
+import org.netbeans.spi.editor.hints.Fix;
+import org.netbeans.spi.java.queries.CompilerOptionsQueryImplementation;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ * Test cases for the Different Case Kinds errors fix.
+ * 
+ */
+public class DifferentCaseKindsFixTest extends ErrorHintsTestBase {
+
+    private static final List<String> EXTRA_OPTIONS = new ArrayList<>();
+
+    public DifferentCaseKindsFixTest(String name) {
+        super(name, DifferentCaseKindsFix.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        sourceLevel = "12";
+        JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true;
+        EXTRA_OPTIONS.add("--enable-preview");
+    }
+
+    @ServiceProvider(service = CompilerOptionsQueryImplementation.class, 
position = 100)
+    public static class TestCompilerOptionsQueryImplementation implements 
CompilerOptionsQueryImplementation {
+
+        @Override
+        public CompilerOptionsQueryImplementation.Result getOptions(FileObject 
file) {
+            return new CompilerOptionsQueryImplementation.Result() {
+                @Override
+                public List<? extends String> getArguments() {
+                    return EXTRA_OPTIONS;
+                }
+
+                @Override
+                public void addChangeListener(ChangeListener listener) {
+                }
+
+                @Override
+                public void removeChangeListener(ChangeListener listener) {
+                }
+            };
+        }
+
+    }
+
+    public void testCase1() throws Exception {
+        performFixTest("test/Test.java",
+                "package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         String result;\n"
+                + "         switch (p) {\n"
+                + "             case 1: result = \"1\"; break;\n"
+                + "             case 2 -> result = \"2\";\n"
+                + "             default -> result = \"3\";\n"
+                + "         }\n"
+                + "     }\n"
+                + "}\n",
+                -1,
+                NbBundle.getMessage(DifferentCaseKindsFix.class, 
"FIX_DifferentCaseKinds"),
+                ("package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         String result;\n"
+                + "         switch (p) {\n"
+                + "             case 1 -> result = \"1\";\n"
+                + "             case 2 -> result = \"2\";\n"
+                + "             default -> result = \"3\";\n"
+                + "         }\n"
+                + "     }\n"
+                + "}\n").replaceAll("[\\s]+", " "));
+    }
+
+    public void testCase2() throws Exception {
+        performFixTest("test/Test.java",
+                "package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         String result;\n"
+                + "         switch (p) {\n"
+                + "             case 1 -> result = \"1\"; break;\n"
+                + "             case 2 -> result = \"2\";\n"
+                + "             default : result = \"3\"; break;\n"
+                + "         }\n"
+                + "     }\n"
+                + "}\n",
+                -1,
+                NbBundle.getMessage(DifferentCaseKindsFix.class, 
"FIX_DifferentCaseKinds"),
+                ("package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         String result;\n"
+                + "         switch (p) {\n"
+                + "             case 1 -> result = \"1\";\n"
+                + "             case 2 -> result = \"2\";\n"
+                + "             default -> result = \"3\";\n"
+                + "         }\n"
+                + "     }\n"
+                + "}\n").replaceAll("[\\s]+", " "));
+    }
+
+    public void testCase3() throws Exception {
+        performFixTest("test/Test.java",
+                "package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         String result;\n"
+                + "         switch (p) {\n"
+                + "             case 1: result = \"1\"; break;\n"
+                + "             case 2: if (true) result = \"2\"; break;\n"
+                + "             case 3 -> { System.err.println(3); result = 
\"3\";}\n"
+                + "         }\n"
+                + "     }\n"
+                + "}\n",
+                -1,
+                NbBundle.getMessage(DifferentCaseKindsFix.class, 
"FIX_DifferentCaseKinds"),
+                ("package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         String result;\n"
+                + "         switch (p) {\n"
+                + "             case 1 -> result = \"1\";\n"
+                + "             case 2 -> { if (true) result = \"2\"; }\n"
+                + "             case 3 -> { System.err.println(3); result = 
\"3\";}\n"
+                + "         }\n"
+                + "     }\n"
+                + "}\n").replaceAll("[\\s]+", " "));
+    }
+    
+    public void testCase4() throws Exception {
+        performFixTest("test/Test.java",
+                "package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         String result;\n"
+                + "         switch (p) {\n"
+                + "             case 0:\n"
+                + "             case 1: result = \"1\"; break;\n"
+                + "             case 2 -> result = \"2\";\n"
+                + "         }\n"
+                + "     }\n"
+                + "}\n",
+                -1,
+                NbBundle.getMessage(DifferentCaseKindsFix.class, 
"FIX_DifferentCaseKinds"),
+                ("package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         String result;\n"
+                + "         switch (p) {\n"
+                + "             case 0, 1 -> result = \"1\";\n"
+                + "             case 2 -> result = \"2\";\n"
+                + "         }\n"
+                + "     }\n"
+                + "}\n").replaceAll("[\\s]+", " "));
+    }
+    
+    public void testCase5() throws Exception {
+        performFixTest("test/Test.java",
+                "package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         String result;\n"
+                + "         switch (p) {\n"
+                + "             case 0 -> {\n"
+                + "                 int i = 0;\n"
+                + "                 int j = 0;\n"
+                + "                 }\n"
+                + "             default:\n"
+                + "                 i = 0;\n"
+                + "                 System.err.println(i);\n"
+                + "                 System.err.println(j = 15);\n"
+                + "                 break;\n"
+                + "         }\n"
+                + "     }\n"
+                + "}\n",
+                -1,
+                NbBundle.getMessage(DifferentCaseKindsFix.class, 
"FIX_DifferentCaseKinds"),
+                ("package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         String result;\n"
+                + "         switch (p) {\n"
+                + "             case 0 -> {\n"
+                + "                 int i = 0;\n"
+                + "                 int j = 0;\n"
+                + "                 }\n"
+                + "             default -> {\n"
+                + "                 i = 0;\n"
+                + "                 System.err.println(i);\n"
+                + "                 System.err.println(j = 15);\n"
+                + "                 }\n"
+                + "         }\n"
+                + "     }\n"
+                + "}\n").replaceAll("[\\s]+", " "));
+    }
+    
+    public void testCase6() throws Exception {
+        performFixTest("test/Test.java",
+                "package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         var result = \n"
+                + "         switch (p) {\n"
+                + "             case 1: break 1;\n"
+                + "             case 2 -> 2;\n"
+                + "             default -> 3;\n"
+                + "         }\n"
+                + "     }\n"
+                + "}\n",
+                -1,
+                NbBundle.getMessage(DifferentCaseKindsFix.class, 
"FIX_DifferentCaseKinds"),
+                ("package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         var result =\n"
+                + "         switch (p) {\n"
+                + "             case 1 -> { break 1; }\n"
+                + "             case 2 -> { break 2; }\n"
+                + "             default -> { break 3; }\n"
+                + "         }\n"
+                + "     }\n"
+                + "}\n").replaceAll("[\\s]+", " "));
+    }
+
+    public void testCase7() throws Exception {
+        performFixTest("test/Test.java",
+                "package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         var result = \n"
+                + "         switch (p) {\n"
+                + "             case 1 -> 1;\n"
+                + "             case 2 -> 2;\n"
+                + "             default : break 3;\n"
+                + "         }\n"
+                + "     }\n"
+                + "}\n",
+                -1,
+                NbBundle.getMessage(DifferentCaseKindsFix.class, 
"FIX_DifferentCaseKinds"),
+                ("package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         var result = \n"
+                + "         switch (p) {\n"
+                + "             case 1 -> { break 1; }\n"
+                + "             case 2 -> { break 2; }\n"
+                + "             default -> { break 3; }\n"
+                + "         }\n"
+                + "     }\n"
+                + "}\n").replaceAll("[\\s]+", " "));
+    }
+
+    public void testCase8() throws Exception {
+        performFixTest("test/Test.java",
+                "package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         var result = \n"
+                + "         switch (p) {\n"
+                + "             case 1: break 1; \n"
+                + "             case 2: break getTest();\n"
+                + "             case 3 -> { System.err.println(3); break 3;}\n"
+                + "         }\n"
+                + "     }\n"
+                + "     private int getTest() {\n"
+                + "         return 10;\n"
+                + "     }\n"
+                + "}\n",
+                -1,
+                NbBundle.getMessage(DifferentCaseKindsFix.class, 
"FIX_DifferentCaseKinds"),
+                ("package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         var result = \n"
+                + "         switch (p) {\n"
+                + "             case 1 -> { break 1; }\n"
+                + "             case 2 -> { break getTest(); }\n"
+                + "             case 3 -> { System.err.println(3); break 3;}\n"
+                + "         }\n"
+                + "     }\n"
+                + "     private int getTest() {\n"
+                + "         return 10;\n"
+                + "     }\n"
+                + "}\n").replaceAll("[\\s]+", " "));
+    }
+    
+    public void testCase9() throws Exception {
+        performFixTest("test/Test.java",
+                "package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         String result = \n"
+                + "         switch (p) {\n"
+                + "             case 0:\n"
+                + "             case 1: break \"1\"; \n"
+                + "             case 2 -> \"2\";\n"
+                + "         }\n"
+                + "     }\n"
+                + "}\n",
+                -1,
+                NbBundle.getMessage(DifferentCaseKindsFix.class, 
"FIX_DifferentCaseKinds"),
+                ("package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         String result = \n"
+                + "         switch (p) {\n"
+                + "             case 0, 1 -> { break \"1\"; }\n"
+                + "             case 2 -> { break \"2\"; }\n"
+                + "         }\n"
+                + "     }\n"
+                + "}\n").replaceAll("[\\s]+", " "));
+    }
+    
+    public void testCase10() throws Exception {
+        performFixTest("test/Test.java",
+                "package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         int result;\n"
+                + "         result = switch (p) {\n"
+                + "             case 1 : \n"
+                + "                 int x =  1;\n"
+                + "                 break x;\n"
+                + "             default -> {\n"
+                + "                 int y =  1;\n"
+                + "                 break 3;\n"
+                + "             }\n"
+                + "         }\n"
+                + "     }\n"
+                + "}\n",
+                -1,
+                NbBundle.getMessage(DifferentCaseKindsFix.class, 
"FIX_DifferentCaseKinds"),
+                ("package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         int result;\n"
+                + "         result = switch (p) {\n"
+                + "             case 1 -> {\n"
+                + "                 int x =  1;\n"
+                + "                 break x;\n"
+                + "             }\n"
+                + "             default -> {\n"
+                + "                 int y =  1;\n"
+                + "                 break 3;\n"
+                + "             }\n"
+                + "         }\n"
+                + "     }\n"
+                + "}\n").replaceAll("[\\s]+", " "));
+    }
+
+    @Override
+    protected List<Fix> computeFixes(CompilationInfo info, int pos, TreePath 
path) throws Exception {
+        return new DifferentCaseKindsFix().run(info, null, pos, path, null);
+    }
+
+    @Override
+    protected Set<String> getSupportedErrorKeys() {
+        return new DifferentCaseKindsFix().getCodes();
+    }
+
+    @Override
+    protected String toDebugString(CompilationInfo info, Fix f) {
+        return f.getText();
+    }
+}
diff --git a/java/java.source.base/apichanges.xml 
b/java/java.source.base/apichanges.xml
index 6d4c376..bc3d971 100644
--- a/java/java.source.base/apichanges.xml
+++ b/java/java.source.base/apichanges.xml
@@ -25,6 +25,18 @@
     <apidef name="javasource_base">Java Source API</apidef>
 </apidefs>
 <changes>
+    <change id="TreeMaker.SwitchExpression">
+        <api name="javasource_base" />
+        <summary>Creates a new SwitchExpressionTree</summary>
+        <version major="1" minor="2.41.0"/>
+        <date day="27" month="2" year="2019"/>
+        <author login="vikasprabhakar"/>
+        <compatibility addition="yes" binary="compatible" source="compatible"/>
+        <description>
+                Creates a new SwitchExpressionTree.
+        </description>
+        <class name="TreeMaker" package="org.netbeans.api.java.source"/>
+    </change>
     <change id="Matcher.setKeepSyntheticTrees">
         <api name="javasource_base"/>
         <summary>Adding Matcher.setKeepSyntheticTrees method</summary>
diff --git a/java/java.source.base/nbproject/project.properties 
b/java/java.source.base/nbproject/project.properties
index 199f22b..6e036de 100644
--- a/java/java.source.base/nbproject/project.properties
+++ b/java/java.source.base/nbproject/project.properties
@@ -23,7 +23,7 @@ javadoc.name=Java Source Base
 javadoc.title=Java Source Base
 javadoc.arch=${basedir}/arch.xml
 javadoc.apichanges=${basedir}/apichanges.xml
-spec.version.base=2.40.0
+spec.version.base=2.41.0
 
test.qa-functional.cp.extra=${refactoring.java.dir}/modules/ext/nb-javac-api.jar
 test.unit.run.cp.extra=${o.n.core.dir}/core/core.jar:\
     ${o.n.core.dir}/lib/boot.jar:\
diff --git 
a/java/java.source.base/src/org/netbeans/api/java/source/TreeMaker.java 
b/java/java.source.base/src/org/netbeans/api/java/source/TreeMaker.java
index 174a820..36592c8 100644
--- a/java/java.source.base/src/org/netbeans/api/java/source/TreeMaker.java
+++ b/java/java.source.base/src/org/netbeans/api/java/source/TreeMaker.java
@@ -1042,7 +1042,19 @@ public final class TreeMaker {
     public SwitchTree Switch(ExpressionTree expression, List<? extends 
CaseTree> cases) {
         return delegate.Switch(expression, cases);
     }
-    
+
+    /**
+     * Creates a new SwitchExpressionTree.
+     *
+     * @param expression the expression which provides the value to be 
switched.
+     * @param cases the list of cases, or an empty list.
+     * @see com.sun.source.tree.SwitchExpressionTree
+     * @since 2.41
+     */
+    public Tree SwitchExpression(ExpressionTree expression, List<? extends 
CaseTree> cases) {
+        return delegate.SwitchExpression(expression, cases);
+    }
+
     /**
      * Creates a new SynchronizedTree.
      *
diff --git 
a/java/java.source.base/src/org/netbeans/api/java/source/WorkingCopy.java 
b/java/java.source.base/src/org/netbeans/api/java/source/WorkingCopy.java
index ff8bc89..6f74af6 100644
--- a/java/java.source.base/src/org/netbeans/api/java/source/WorkingCopy.java
+++ b/java/java.source.base/src/org/netbeans/api/java/source/WorkingCopy.java
@@ -864,6 +864,8 @@ public class WorkingCopy extends CompilationController {
                     Tree t;
                     if (translated != null) {
                         t = translate(translated);
+                    } else if (tree != null && 
tree.getKind().toString().equals("SWITCH_EXPRESSION")) {
+                        t = visitSwitchExpression(tree, null);
                     } else {
                         t = super.translate(tree);
                     }
@@ -886,6 +888,10 @@ public class WorkingCopy extends CompilationController {
                     }
                     return super.translate(tree);
                 }
+
+                public Tree visitSwitchExpression(Tree set, Object p) {
+                    return rewriteChildren(set);
+                }
             };
             Context c = impl.getJavacTask().getContext();
             itt.attach(c, ia, tree2Tag);
diff --git 
a/java/java.source.base/src/org/netbeans/modules/java/source/TreeShims.java 
b/java/java.source.base/src/org/netbeans/modules/java/source/TreeShims.java
index 73d9c1c..9dbd309 100644
--- a/java/java.source.base/src/org/netbeans/modules/java/source/TreeShims.java
+++ b/java/java.source.base/src/org/netbeans/modules/java/source/TreeShims.java
@@ -25,6 +25,8 @@ import com.sun.source.tree.StatementTree;
 import com.sun.source.tree.SwitchTree;
 import com.sun.source.tree.Tree;
 import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.TreeMaker;
+import com.sun.tools.javac.util.ListBuffer;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
@@ -112,6 +114,19 @@ public class TreeShims {
         }
     }
 
+    public static Tree SwitchExpression(TreeMaker make, ExpressionTree 
selector, List<? extends CaseTree> caseList) throws SecurityException {
+        ListBuffer<JCTree.JCCase> cases = new ListBuffer<JCTree.JCCase>();
+        for (CaseTree t : caseList) {
+            cases.append((JCTree.JCCase) t);
+        }
+        try {
+            Method getMethod = 
TreeMaker.class.getDeclaredMethod("SwitchExpression", 
JCTree.JCExpression.class, com.sun.tools.javac.util.List.class);
+            return (Tree) getMethod.invoke(make, (JCTree.JCExpression) 
selector, cases.toList());
+        } catch (NoSuchMethodException | IllegalAccessException | 
IllegalArgumentException | InvocationTargetException ex) {
+            throw TreeShims.<RuntimeException>throwAny(ex);
+        }
+    }
+  
     @SuppressWarnings("unchecked")
     private static <T extends Throwable> RuntimeException throwAny(Throwable 
t) throws T {
         throw (T) t;
diff --git 
a/java/java.source.base/src/org/netbeans/modules/java/source/builder/TreeFactory.java
 
b/java/java.source.base/src/org/netbeans/modules/java/source/builder/TreeFactory.java
index 8157cec..7fd32f9 100644
--- 
a/java/java.source.base/src/org/netbeans/modules/java/source/builder/TreeFactory.java
+++ 
b/java/java.source.base/src/org/netbeans/modules/java/source/builder/TreeFactory.java
@@ -80,6 +80,7 @@ import javax.lang.model.util.Elements;
 import javax.lang.model.util.Types;
 import javax.tools.JavaFileObject;
 import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.modules.java.source.TreeShims;
 import static org.netbeans.modules.java.source.save.PositionEstimator.*;
 
 /**
@@ -803,7 +804,11 @@ public class TreeFactory {
             cases.append((JCCase)t);
         return make.at(NOPOS).Switch((JCExpression)expression, cases.toList());
     }
-    
+
+    public Tree SwitchExpression(ExpressionTree expression, List<? extends 
CaseTree> caseList) {
+        return TreeShims.SwitchExpression(make.at(NOPOS), expression, 
caseList);
+    }
+
     public SynchronizedTree Synchronized(ExpressionTree expression, BlockTree 
block) {
         return make.at(NOPOS).Synchronized((JCExpression)expression, 
(JCBlock)block);
     }
diff --git 
a/java/java.source.base/src/org/netbeans/modules/java/source/matching/CopyFinder.java
 
b/java/java.source.base/src/org/netbeans/modules/java/source/matching/CopyFinder.java
index 303ff24..3e0b46b 100644
--- 
a/java/java.source.base/src/org/netbeans/modules/java/source/matching/CopyFinder.java
+++ 
b/java/java.source.base/src/org/netbeans/modules/java/source/matching/CopyFinder.java
@@ -71,6 +71,7 @@ import com.sun.source.tree.WhileLoopTree;
 import com.sun.source.tree.WildcardTree;
 import com.sun.source.util.SourcePositions;
 import com.sun.source.util.TreePath;
+import com.sun.tools.javac.tree.JCTree;
 import org.netbeans.api.java.source.support.ErrorAwareTreeScanner;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -100,6 +101,7 @@ import javax.lang.model.type.TypeMirror;
 import org.netbeans.api.annotations.common.CheckForNull;
 import org.netbeans.api.annotations.common.NonNull;
 import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.modules.java.source.TreeShims;
 
 /**TODO: tested by CopyFinderTest in java.hints module.
  *
@@ -1789,7 +1791,14 @@ public class CopyFinder extends 
ErrorAwareTreeScanner<Boolean, TreePath> {
             case BLOCK:
                 return ((BlockTree) 
firstLeaf.getParentPath().getLeaf()).getStatements();
             case CASE:
-                return ((CaseTree) 
firstLeaf.getParentPath().getLeaf()).getStatements();
+                CaseTree caseTree = (CaseTree) 
firstLeaf.getParentPath().getLeaf();
+                if (caseTree.getStatements() != null) {
+                    return caseTree.getStatements();
+                } else if (TreeShims.getBody(caseTree) instanceof 
StatementTree) {
+                    return Collections.singletonList((StatementTree) 
TreeShims.getBody(caseTree));
+                } else {
+                    return null;
+                }
             default:
                 return Collections.singletonList((StatementTree) 
firstLeaf.getLeaf());
         }
diff --git 
a/java/java.source.base/src/org/netbeans/modules/java/source/pretty/VeryPretty.java
 
b/java/java.source.base/src/org/netbeans/modules/java/source/pretty/VeryPretty.java
index 667dbd6..793a388 100644
--- 
a/java/java.source.base/src/org/netbeans/modules/java/source/pretty/VeryPretty.java
+++ 
b/java/java.source.base/src/org/netbeans/modules/java/source/pretty/VeryPretty.java
@@ -103,6 +103,7 @@ import org.netbeans.api.java.source.Comment;
 import org.netbeans.api.java.source.Comment.Style;
 import org.netbeans.api.lexer.TokenHierarchy;
 import org.netbeans.api.lexer.TokenSequence;
+import org.netbeans.modules.java.source.TreeShims;
 import org.netbeans.modules.java.source.builder.CommentHandlerService;
 import org.netbeans.modules.java.source.query.CommentHandler;
 import org.netbeans.modules.java.source.query.CommentSet;
@@ -116,8 +117,6 @@ import org.netbeans.modules.java.source.save.Reformatter;
 import org.netbeans.modules.java.source.transform.FieldGroupTree;
 import org.netbeans.spi.java.classpath.support.ClassPathSupport;
 
-import org.openide.util.Exceptions;
-
 /** Prints out a tree as an indented Java source program.
  */
 public final class VeryPretty extends JCTree.Visitor implements 
DocTreeVisitor<Void, Void>, TrimBufferObserver {
@@ -1436,13 +1435,15 @@ public final class VeryPretty extends JCTree.Visitor 
implements DocTreeVisitor<V
 
     @Override
     public void visitBreak(JCBreak tree) {
-       print("break");
-        //TODO: value breaks
-       if (tree.getLabel() != null) {
-           needSpace();
-           print(tree.getLabel());
-       }
-       print(';');
+        print("break");
+        if (TreeShims.getValue(tree) != null) {
+            needSpace();
+            print((JCTree) TreeShims.getValue(tree));
+        } else if (tree.getLabel() != null) {
+            needSpace();
+            print(tree.getLabel());
+        }
+        print(';');
     }
 
     @Override
diff --git 
a/java/java.source.base/src/org/netbeans/modules/java/source/save/CasualDiff.java
 
b/java/java.source.base/src/org/netbeans/modules/java/source/save/CasualDiff.java
index 7daa817..87377e3 100644
--- 
a/java/java.source.base/src/org/netbeans/modules/java/source/save/CasualDiff.java
+++ 
b/java/java.source.base/src/org/netbeans/modules/java/source/save/CasualDiff.java
@@ -125,6 +125,7 @@ import com.sun.tools.javac.tree.JCTree.TypeBoundKind;
 import com.sun.tools.javac.tree.Pretty;
 import com.sun.tools.javac.tree.TreeInfo;
 import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.ListBuffer;
 import com.sun.tools.javac.util.Name;
 import com.sun.tools.javac.util.Names;
 import com.sun.tools.javac.util.Position;
@@ -167,6 +168,7 @@ import org.openide.util.Exceptions;
 import org.openide.util.NbBundle;
 import org.openide.util.NbCollections;
 import javax.lang.model.type.TypeKind;
+import org.netbeans.modules.java.source.TreeShims;
 
 public class CasualDiff {
 
@@ -1874,6 +1876,31 @@ public class CasualDiff {
         return bounds[1];
     }
 
+    protected int diffSwitchExpression(Tree oldT, Tree newT, int[] bounds) {
+        int localPointer = bounds[0];
+
+        // rename in switch
+        int[] selectorBounds = 
getBounds((JCTree)TreeShims.getExpressions(oldT).get(0));
+        copyTo(localPointer, selectorBounds[0]);
+        localPointer = diffTree((JCTree)TreeShims.getExpressions(oldT).get(0), 
(JCTree)TreeShims.getExpressions(newT).get(0), selectorBounds);
+
+        tokenSequence.move(selectorBounds[1]);
+        do { } while (tokenSequence.moveNext() && JavaTokenId.LBRACE != 
tokenSequence.token().id());
+        tokenSequence.moveNext();
+        copyTo(localPointer, localPointer = tokenSequence.offset());
+        PositionEstimator est = 
EstimatorFactory.cases(TreeShims.getCases(oldT), TreeShims.getCases(newT), 
diffContext);
+        ListBuffer<JCTree.JCCase> oldTcases = new ListBuffer<JCTree.JCCase>();
+        for (CaseTree t : TreeShims.getCases(oldT))
+            oldTcases.append((JCTree.JCCase)t);
+        ListBuffer<JCTree.JCCase> newTcases = new ListBuffer<JCTree.JCCase>();
+        for (CaseTree t : TreeShims.getCases(newT))
+            newTcases.append((JCTree.JCCase)t);
+        localPointer = diffList(oldTcases.toList(), newTcases.toList(), 
localPointer, est, Measure.MEMBER, printer);
+
+        copyTo(localPointer, bounds[1]);
+        return bounds[1];
+    }
+
     protected int diffCase(JCCase oldT, JCCase newT, int[] bounds) {
         int localPointer = bounds[0];
         List<JCExpression> oldPatterns = getCasePatterns(oldT);
@@ -5585,6 +5612,10 @@ public class CasualDiff {
                   }
                   break;
               }
+              if(oldT.getKind().toString().equals("SWITCH_EXPRESSION")){
+                  retVal = diffSwitchExpression(oldT, newT, elementBounds);
+                  break;
+              }
               String msg = "Diff not implemented: " +
                   ((com.sun.source.tree.Tree)oldT).getKind().toString() +
                   " " + oldT.getClass().getName();
diff --git 
a/java/java.source.base/src/org/netbeans/modules/java/source/transform/ImmutableTreeTranslator.java
 
b/java/java.source.base/src/org/netbeans/modules/java/source/transform/ImmutableTreeTranslator.java
index ce0648d..a88916a 100644
--- 
a/java/java.source.base/src/org/netbeans/modules/java/source/transform/ImmutableTreeTranslator.java
+++ 
b/java/java.source.base/src/org/netbeans/modules/java/source/transform/ImmutableTreeTranslator.java
@@ -805,6 +805,19 @@ public class ImmutableTreeTranslator implements 
TreeVisitor<Tree,Object> {
        return tree;
     }
 
+    protected final Tree rewriteChildren(Tree tree) {
+       ExpressionTree selector = 
(ExpressionTree)translate(TreeShims.getExpressions(tree).get(0));
+       List<? extends CaseTree> cases = 
translateStable(TreeShims.getCases(tree));
+       if (selector != TreeShims.getExpressions(tree).get(0) || 
!cases.equals(TreeShims.getCases(tree))) {
+           Tree switchExpression = make.SwitchExpression(selector, cases);
+            model.setType(switchExpression, model.getType(tree));
+           copyCommentTo(tree,switchExpression);
+            copyPosTo(tree,switchExpression);
+           tree = switchExpression;
+       }
+       return tree;
+    }
+
     protected final CaseTree rewriteChildren(CaseTree tree) {
         Tree body = TreeShims.getBody(tree);
         List<? extends ExpressionTree> expressions = 
TreeShims.getExpressions(tree);
diff --git 
a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/gen/SwitchExpressionTest.java
 
b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/gen/SwitchExpressionTest.java
new file mode 100644
index 0000000..81a3897
--- /dev/null
+++ 
b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/gen/SwitchExpressionTest.java
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.api.java.source.gen;
+
+import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.CaseTree;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.StatementTree;
+import com.sun.source.tree.SwitchTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.VariableTree;
+import com.sun.tools.javac.tree.JCTree;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import javax.lang.model.element.Element;
+import javax.swing.event.ChangeListener;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.Task;
+import org.netbeans.api.java.source.TestUtilities;
+import org.netbeans.api.java.source.TreeMaker;
+import org.netbeans.api.java.source.WorkingCopy;
+import org.netbeans.junit.NbTestSuite;
+import org.netbeans.modules.java.source.TreeShims;
+import org.netbeans.modules.java.source.parsing.JavacParser;
+import org.netbeans.spi.java.queries.CompilerOptionsQueryImplementation;
+import org.openide.filesystems.FileObject;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ * Test cases for SwitchExpression
+ *
+ */
+public class SwitchExpressionTest extends TreeRewriteTestBase {
+
+    private static final List<String> EXTRA_OPTIONS = new ArrayList<>();
+
+    public SwitchExpressionTest(String testName) {
+        super(testName);
+    }
+
+    public static NbTestSuite suite() {
+        NbTestSuite suite = new NbTestSuite();
+        suite.addTestSuite(SwitchExpressionTest.class);
+        return suite;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        sourceLevel = "1.12";
+        JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true;
+        EXTRA_OPTIONS.add("--enable-preview");
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = false;
+
+    }
+
+    @ServiceProvider(service = CompilerOptionsQueryImplementation.class, 
position = 100)
+    public static class TestCompilerOptionsQueryImplementation implements 
CompilerOptionsQueryImplementation {
+
+        @Override
+        public CompilerOptionsQueryImplementation.Result getOptions(FileObject 
file) {
+            return new CompilerOptionsQueryImplementation.Result() {
+                @Override
+                public List<? extends String> getArguments() {
+                    return EXTRA_OPTIONS;
+                }
+
+                @Override
+                public void addChangeListener(ChangeListener listener) {
+                }
+
+                @Override
+                public void removeChangeListener(ChangeListener listener) {
+                }
+            };
+        }
+
+    }
+
+    public void testSwitchExpression() throws Exception {
+
+        String code = "package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         var v = switch (p) {\n"
+                + "             case 1: break 1;\n"
+                + "             case 2 -> 2;\n"
+                + "             default -> 3;\n"
+                + "         }\n"
+                + "     }\n"
+                + "}\n";
+        String golden = "package test; \n"
+                + "public class Test {\n"
+                + "     private void test(int p) {\n"
+                + "         var v = switch (p) {\n"
+                + "             case 1 -> {\n"
+                + "                 break 1;\n"
+                + "             }\n"
+                + "             case 2 -> {\n"
+                + "                 break 2;\n"
+                + "             }\n"
+                + "             default -> {\n"
+                + "                 break 3;\n"
+                + "             }\n"
+                + "         }\n"
+                + "     }\n"
+                + "}\n";
+
+        prepareTest("Test", code);
+
+        rewriteSwitchExpression();
+        String res = TestUtilities.copyFileToString(getTestFile());
+        System.err.println(res);
+        assertEquals(golden, res);
+
+    }
+
+    /**
+     * Rewrite Switch Expression cases.
+     *
+     * @throws IOException
+     */
+    private void rewriteSwitchExpression() throws IOException {
+
+        JavaSource js = getJavaSource();
+        assertNotNull(js);
+
+        Task<WorkingCopy> task = new Task<WorkingCopy>() {
+
+            public void run(WorkingCopy workingCopy) throws IOException {
+                workingCopy.toPhase(JavaSource.Phase.RESOLVED);
+                CompilationUnitTree cut = workingCopy.getCompilationUnit();
+                TreeMaker make = workingCopy.getTreeMaker();
+                ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
+                MethodTree method = (MethodTree) clazz.getMembers().get(1);
+                List<CaseTree> newCases = new ArrayList<>();
+                VariableTree switcExpression = (VariableTree) ((BlockTree) 
method.getBody()).getStatements().get(0);
+                Tree switchBlock = switcExpression.getInitializer();
+                List<? extends CaseTree> cases;
+                List<ExpressionTree> patterns = new ArrayList<>();
+                boolean switchExpressionFlag = 
switchBlock.getKind().toString().equals("SWITCH_EXPRESSION");
+                if (switchExpressionFlag) {
+                    cases = TreeShims.getCases(switchBlock);
+                } else {
+                    cases = ((SwitchTree) switchBlock).getCases();
+                }
+                for (Iterator<? extends CaseTree> it = cases.iterator(); 
it.hasNext();) {
+                    CaseTree ct = it.next();
+                    patterns.addAll(TreeShims.getExpressions(ct));
+                    List<StatementTree> statements;
+                    if (ct.getStatements() == null) {
+                        statements = new ArrayList<>(((JCTree.JCCase) 
ct).stats);
+                    } else {
+                        statements = new ArrayList<>(ct.getStatements());
+                    }
+                    if (statements.isEmpty()) {
+                        if (it.hasNext()) {
+                            continue;
+                        }
+                    }
+                    Set<Element> seenVariables = new HashSet<>();
+                    int idx = 0;
+                    for (StatementTree statement : new 
ArrayList<>(statements)) {
+                        Tree body = make.Block(statements, false);
+                        if (statements.size() == 1) {
+                            if (statements.get(0).getKind() == 
Tree.Kind.EXPRESSION_STATEMENT
+                                    || statements.get(0).getKind() == 
Tree.Kind.THROW
+                                    || statements.get(0).getKind() == 
Tree.Kind.BLOCK) {
+                                body = statements.get(0);
+                            }
+                        }
+                        newCases.add(make.Case(patterns, body));
+                        patterns = new ArrayList<>();
+                    }
+                }
+                workingCopy.rewrite(switchBlock, 
make.SwitchExpression(TreeShims.getExpressions(switchBlock).get(0), newCases));
+            }
+
+        };
+
+        js.runModificationTask(task).commit();
+    }
+
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@netbeans.apache.org
For additional commands, e-mail: commits-h...@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists

Reply via email to