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 <[email protected]>
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: [email protected]
For additional commands, e-mail: [email protected]
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists