This is an automated email from the ASF dual-hosted git repository.
akhileshsingh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new 6beddee262 Implementation of auto completion, formatting adjustment
and providing hints for JEP 405 record patterns (#4533)
6beddee262 is described below
commit 6beddee262e8bdf6f2d0f94b2085a797d85dc23f
Author: Meghna Jayan <[email protected]>
AuthorDate: Wed Oct 19 12:48:13 2022 +0530
Implementation of auto completion, formatting adjustment and providing
hints for JEP 405 record patterns (#4533)
* Add autocompletion for Record Patterns
---
.../java/completion/JavaCompletionTask.java | 26 +-
.../1.8/AutoCompletion_RecordPattern_1.pass | 1 +
.../1.8/AutoCompletion_RecordPattern_2.pass | 1 +
.../1.8/AutoCompletion_RecordPattern_3.pass | 1 +
.../19/AutoCompletion_RecordPattern_1.pass | 1 +
.../19/AutoCompletion_RecordPattern_2.pass | 1 +
.../19/AutoCompletion_RecordPattern_3.pass | 1 +
.../java/completion/data/RecordPattern.java | 44 ++++
.../java/completion/CompletionTestBase.java | 30 ++-
.../JavaCompletionTask119FeaturesTest.java | 26 +-
.../modules/editor/java/JavaCompletionItem.java | 55 +++-
.../editor/java/JavaCompletionItemFactory.java | 8 +-
.../hints/jdk/ConvertToNestedRecordPattern.java | 278 +++++++++++++++++++++
.../java/hints/jdk/ConvertToRecordPattern.java | 194 ++++++++++++++
.../jdk/ConvertToNestedRecordPatternTest.java | 159 ++++++++++++
.../java/hints/jdk/ConvertToRecordPatternTest.java | 145 +++++++++++
.../org/netbeans/api/java/source/TreeMaker.java | 13 +
.../netbeans/modules/java/source/TreeShims.java | 58 +++++
.../modules/java/source/pretty/VeryPretty.java | 23 +-
.../modules/java/source/save/CasualDiff.java | 26 ++
.../modules/java/source/save/Reformatter.java | 4 +-
.../modules/java/source/save/FormatingTest.java | 177 +++++++++++++
22 files changed, 1259 insertions(+), 13 deletions(-)
diff --git
a/java/java.completion/src/org/netbeans/modules/java/completion/JavaCompletionTask.java
b/java/java.completion/src/org/netbeans/modules/java/completion/JavaCompletionTask.java
index 422efaa3c1..d4298e774f 100644
---
a/java/java.completion/src/org/netbeans/modules/java/completion/JavaCompletionTask.java
+++
b/java/java.completion/src/org/netbeans/modules/java/completion/JavaCompletionTask.java
@@ -130,6 +130,10 @@ public final class JavaCompletionTask<T> extends BaseTask {
T createModuleItem(String moduleName, int substitutionOffset);
}
+ public static interface RecordPatternItemFactory<T> extends ItemFactory<T>
{
+ T createRecordPatternItem(CompilationInfo info, TypeElement elem,
DeclaredType type, int substitutionOffset, ReferencesCount referencesCount,
boolean isDeprecated, boolean insideNew, boolean addTypeVars);
+ }
+
public static enum Options {
ALL_COMPLETION,
@@ -493,6 +497,9 @@ public final class JavaCompletionTask<T> extends BaseTask {
localResult(env);
addKeywordsForBlock(env);
break;
+ case DECONSTRUCTION_PATTERN:
+ insideDeconstructionRecordPattern(env);
+ break;
case PARENTHESIZED_PATTERN:
insideParenthesizedPattern(env);
break;
@@ -3250,6 +3257,16 @@ public final class JavaCompletionTask<T> extends
BaseTask {
}
+ private void insideDeconstructionRecordPattern(final Env env) throws
IOException {
+ final CompilationController controller = env.getController();
+ final Elements elements = controller.getElements();
+ TypeMirror tm =
controller.getTreeUtilities().parseType(env.getPrefix(),
env.getScope().getEnclosingClass());
+ TypeElement e = (TypeElement) ((DeclaredType) tm).asElement();
+ if (e.getSimpleName().toString().contentEquals(env.getPrefix()) &&
(e.getKind().equals(ElementKind.RECORD))) {
+ results.add(((RecordPatternItemFactory<T>)
itemFactory).createRecordPatternItem(env.getController(), e, (DeclaredType)
e.asType(), anchorOffset, null, elements.isDeprecated(e), env.isInsideNew(),
env.isInsideNew() || env.isInsideClass()));
+ }
+ }
+
private void insideParenthesizedPattern(Env env) {
final int offset = env.getOffset();
final CompilationController controller = env.getController();
@@ -4234,7 +4251,14 @@ public final class JavaCompletionTask<T> extends
BaseTask {
}
};
for (TypeElement e :
controller.getElementUtilities().getGlobalTypes(acceptor)) {
- results.add(itemFactory.createTypeItem(env.getController(), e,
(DeclaredType) e.asType(), anchorOffset, null, elements.isDeprecated(e),
env.isInsideNew(), env.isInsideNew() || env.isInsideClass(), false, false,
false));
+ Tree iot = env.getPath().getLeaf();
+ TokenSequence<JavaTokenId> ts = findLastNonWhitespaceToken(env,
iot, env.getOffset());
+ if (env.getPrefix() != null &&
e.getSimpleName().toString().contentEquals(env.getPrefix()) &&
(e.getKind().equals(ElementKind.RECORD))
+ && ts != null && ts.token().id() ==
JavaTokenId.INSTANCEOF) {
+ results.add(((RecordPatternItemFactory<T>)
itemFactory).createRecordPatternItem(controller, e, (DeclaredType) e.asType(),
anchorOffset, null, controller.getElements().isDeprecated(e),
env.isInsideNew(), env.isInsideNew() || env.isInsideClass()));
+ } else {
+ results.add(itemFactory.createTypeItem(env.getController(), e,
(DeclaredType) e.asType(), anchorOffset, null, elements.isDeprecated(e),
env.isInsideNew(), env.isInsideNew() || env.isInsideClass(), false, false,
false));
+ }
}
}
diff --git
a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/AutoCompletion_RecordPattern_1.pass
b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/AutoCompletion_RecordPattern_1.pass
new file mode 100644
index 0000000000..6daf41cb8c
--- /dev/null
+++
b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/AutoCompletion_RecordPattern_1.pass
@@ -0,0 +1 @@
+Person(String name, int a)
\ No newline at end of file
diff --git
a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/AutoCompletion_RecordPattern_2.pass
b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/AutoCompletion_RecordPattern_2.pass
new file mode 100644
index 0000000000..a1346a9156
--- /dev/null
+++
b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/AutoCompletion_RecordPattern_2.pass
@@ -0,0 +1 @@
+Rect(ColoredPoint upperLeft, ColoredPoint lr)
\ No newline at end of file
diff --git
a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/AutoCompletion_RecordPattern_3.pass
b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/AutoCompletion_RecordPattern_3.pass
new file mode 100644
index 0000000000..3cd138f22b
--- /dev/null
+++
b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/AutoCompletion_RecordPattern_3.pass
@@ -0,0 +1 @@
+ColoredPoint(Point p, Color c)
\ No newline at end of file
diff --git
a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/19/AutoCompletion_RecordPattern_1.pass
b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/19/AutoCompletion_RecordPattern_1.pass
new file mode 100644
index 0000000000..6daf41cb8c
--- /dev/null
+++
b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/19/AutoCompletion_RecordPattern_1.pass
@@ -0,0 +1 @@
+Person(String name, int a)
\ No newline at end of file
diff --git
a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/19/AutoCompletion_RecordPattern_2.pass
b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/19/AutoCompletion_RecordPattern_2.pass
new file mode 100644
index 0000000000..a1346a9156
--- /dev/null
+++
b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/19/AutoCompletion_RecordPattern_2.pass
@@ -0,0 +1 @@
+Rect(ColoredPoint upperLeft, ColoredPoint lr)
\ No newline at end of file
diff --git
a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/19/AutoCompletion_RecordPattern_3.pass
b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/19/AutoCompletion_RecordPattern_3.pass
new file mode 100644
index 0000000000..3cd138f22b
--- /dev/null
+++
b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/19/AutoCompletion_RecordPattern_3.pass
@@ -0,0 +1 @@
+ColoredPoint(Point p, Color c)
\ No newline at end of file
diff --git
a/java/java.completion/test/unit/data/org/netbeans/modules/java/completion/data/RecordPattern.java
b/java/java.completion/test/unit/data/org/netbeans/modules/java/completion/data/RecordPattern.java
new file mode 100644
index 0000000000..1cc92ac252
--- /dev/null
+++
b/java/java.completion/test/unit/data/org/netbeans/modules/java/completion/data/RecordPattern.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+/**
+ *
+ * @author mjayan
+ */
+public class RecordPattern {
+ public void op(Object o) {
+ if(o instanceof Person) {
+ System.out.println("Hello");
+ }
+ if(o instanceof Rect r){
+ System.out.println("Hy");
+ }
+ if(o instanceof Rect(ColoredPoint upperLeft,ColoredPoint lr)){
+ System.out.println("Hy");
+ }
+ }
+}
+record Person(String name, int a) {}
+record Point(int x, int y) {}
+record Check(int x, String y) {}
+record Rect(ColoredPoint upperLeft,ColoredPoint lr) {}
+record ColoredPoint(Point p, Color c) {}
+enum Color {
+ RED,GREEN,BLUE
+ }
diff --git
a/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/CompletionTestBase.java
b/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/CompletionTestBase.java
index 73247b09bd..fdba908c90 100644
---
a/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/CompletionTestBase.java
+++
b/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/CompletionTestBase.java
@@ -112,7 +112,7 @@ public class CompletionTestBase extends
CompletionTestBaseBase {
LifecycleManager.getDefault().saveAll();
}
- private static class CIFactory implements
JavaCompletionTask.ModuleItemFactory<CI> {
+ private static class CIFactory implements
JavaCompletionTask.ModuleItemFactory<CI>,
JavaCompletionTask.RecordPatternItemFactory<CI> {
private static final int SMART_TYPE = 1000;
@Override
@@ -438,6 +438,34 @@ public class CompletionTestBase extends
CompletionTestBaseBase {
String simpleName = elem.getSimpleName().toString();
return new CI(simpleName + "()", smartType ? 650 - SMART_TYPE :
650, simpleName + "#0#");
}
+
+ @Override
+ public CI createRecordPatternItem(CompilationInfo info, TypeElement
elem, DeclaredType type, int substitutionOffset, ReferencesCount
referencesCount, boolean isDeprecated, boolean insideNew, boolean addTypeVars) {
+ String simpleName = elem.getSimpleName().toString();
+ StringBuilder sb = new StringBuilder();
+ StringBuilder sortParams = new StringBuilder();
+ sb.append(simpleName);
+ sb.append('(');
+ sortParams.append('(');
+ Iterator<? extends RecordComponentElement> it =
elem.getRecordComponents().iterator();
+ RecordComponentElement recordComponent;
+ String name;
+ int cnt = 0;
+ while (it.hasNext()) {
+ recordComponent = it.next();
+ name =
recordComponent.getAccessor().getReturnType().toString();
+ sortParams.append(name);
+ sb.append(name.substring(name.lastIndexOf(".") + 1));
+ sb.append(" ");
+ sb.append(recordComponent.getSimpleName().toString());
+ if (it.hasNext()) {
+ sb.append(", "); //NOI18N
+ }
+ cnt++;
+ }
+ sb.append(")");
+ return new CI(sb.toString(), 650, "#" + ((cnt < 10 ? "0" : "") +
cnt) + "#" + sortParams.toString());
+ }
@Override
public CI createParametersItem(CompilationInfo info, ExecutableElement
elem, ExecutableType type, int substitutionOffset, boolean isDeprecated, int
activeParamIndex, String name) {
diff --git
a/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaCompletionTask119FeaturesTest.java
b/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaCompletionTask119FeaturesTest.java
index 231bee5eff..5cb9fecfa4 100644
---
a/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaCompletionTask119FeaturesTest.java
+++
b/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaCompletionTask119FeaturesTest.java
@@ -1,4 +1,3 @@
-package org.netbeans.modules.java.completion;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@@ -17,7 +16,7 @@ package org.netbeans.modules.java.completion;
* specific language governing permissions and limitations
* under the License.
*/
-
+package org.netbeans.modules.java.completion;
import java.util.ArrayList;
import java.util.List;
@@ -51,6 +50,24 @@ public class JavaCompletionTask119FeaturesTest extends
CompletionTestBase {
return suite;
}
+ public void testRecordPatternCompletion_1() throws Exception {
+
TestCompilerOptionsQueryImplementation.EXTRA_OPTIONS.add("--enable-preview");
+ performTest("RecordPattern", 930, null,
"AutoCompletion_RecordPattern_1.pass", getLatestSource());
+ }
+
+ public void testRecordPatternCompletion_2() throws Exception {
+
TestCompilerOptionsQueryImplementation.EXTRA_OPTIONS.add("--enable-preview");
+ performTest("RecordPattern", 1013, null,
"AutoCompletion_RecordPattern_2.pass", getLatestSource());
+ }
+
+ public void testRecordPatternCompletion_3() throws Exception {
+
TestCompilerOptionsQueryImplementation.EXTRA_OPTIONS.add("--enable-preview");
+ performTest("RecordPattern", 1107, null,
"AutoCompletion_RecordPattern_3.pass", getLatestSource());
+ }
+
+ private String getLatestSource() {
+ return
SourceVersion.latest().name().substring(SourceVersion.latest().name().indexOf("_")
+ 1);
+ }
public void testCasePatternGuard_1() throws Exception {
TestCompilerOptionsQueryImplementation.EXTRA_OPTIONS.add("--enable-preview");
@@ -69,12 +86,9 @@ public class JavaCompletionTask119FeaturesTest extends
CompletionTestBase {
JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true;
}
- private String getLatestSource() {
- return
SourceVersion.latest().name().substring(SourceVersion.latest().name().indexOf("_")+1);
- }
-
@ServiceProvider(service = CompilerOptionsQueryImplementation.class,
position = 100)
public static class TestCompilerOptionsQueryImplementation implements
CompilerOptionsQueryImplementation {
+
private static final List<String> EXTRA_OPTIONS = new ArrayList<>();
@Override
diff --git
a/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionItem.java
b/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionItem.java
index 1b7f7d3015..aa52558e64 100644
---
a/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionItem.java
+++
b/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionItem.java
@@ -128,6 +128,15 @@ public abstract class JavaCompletionItem implements
CompletionItem {
}
}
+ public static JavaCompletionItem createRecordPatternItem(CompilationInfo
info, TypeElement elem, DeclaredType type, int substitutionOffset,
ReferencesCount referencesCount, boolean isDeprecated, boolean insideNew,
boolean addTypeVars) {
+ if(elem.getKind().equals(ElementKind.RECORD)) {
+ return new RecordPatternItem(info, elem, type, 0,
substitutionOffset, referencesCount, isDeprecated, insideNew);
+ }
+ else {
+ throw new IllegalArgumentException("kind=" + elem.getKind());
+ }
+ }
+
public static JavaCompletionItem createArrayItem(CompilationInfo info,
ArrayType type, int substitutionOffset, ReferencesCount referencesCount,
Elements elements, WhiteListQuery.WhiteList whiteList) {
int dim = 0;
TypeMirror tm = type;
@@ -1336,7 +1345,51 @@ public abstract class JavaCompletionItem implements
CompletionItem {
return icon;
}
}
-
+
+ static class RecordPatternItem extends ClassItem {
+
+ private String simpleName;
+ private String recordParams;
+
+ private RecordPatternItem(CompilationInfo info, TypeElement elem,
DeclaredType type, int dim, int substitutionOffset, ReferencesCount
referencesCount, boolean isDeprecated, boolean insideNew) {
+ super(info, elem, type, dim, substitutionOffset, referencesCount,
isDeprecated, insideNew, false, false, false, false, null);
+ simpleName = elem.getSimpleName().toString();
+ Iterator<? extends RecordComponentElement> it =
elem.getRecordComponents().iterator();
+ StringBuilder sb = new StringBuilder();
+ RecordComponentElement recordComponent;
+ String name;
+ sb.append("(");
+ while (it.hasNext()) {
+ recordComponent = it.next();
+ name =
recordComponent.getAccessor().getReturnType().toString();
+ name = name.substring(name.lastIndexOf(".") + 1);
+ sb.append(name);
+ sb.append(" ");
+ sb.append(recordComponent.getSimpleName().toString());
+ if (it.hasNext()) {
+ sb.append(", "); //NOI18N
+ }
+ }
+ sb.append(")");
+ recordParams = sb.toString();
+ }
+
+ @Override
+ protected CharSequence substituteText(final JTextComponent c, final
int offset, final int length, final CharSequence text, final CharSequence
toAdd) {
+ return recordParams;
+ }
+
+ @Override
+ protected String getLeftHtmlText() {
+ return simpleName + recordParams;
+ }
+
+ @Override
+ public int getSortPriority() {
+ return 650;
+ }
+ }
+
static class AnnotationTypeItem extends ClassItem {
private static final String ANNOTATION =
"org/netbeans/modules/editor/resources/completion/annotation_type.png"; //
NOI18N
diff --git
a/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionItemFactory.java
b/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionItemFactory.java
index 535a555dfb..41836d865f 100644
---
a/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionItemFactory.java
+++
b/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionItemFactory.java
@@ -48,7 +48,8 @@ import org.openide.filesystems.FileObject;
*/
public final class JavaCompletionItemFactory implements
JavaCompletionTask.TypeCastableItemFactory<JavaCompletionItem>,
JavaCompletionTask.LambdaItemFactory<JavaCompletionItem>,
- JavaCompletionTask.ModuleItemFactory<JavaCompletionItem> {
+ JavaCompletionTask.ModuleItemFactory<JavaCompletionItem>,
+ JavaCompletionTask.RecordPatternItemFactory<JavaCompletionItem> {
private final WhiteListQuery.WhiteList whiteList;
@@ -180,4 +181,9 @@ public final class JavaCompletionItemFactory implements
JavaCompletionTask.TypeC
public JavaCompletionItem createLambdaItem(CompilationInfo info,
TypeElement elem, DeclaredType type, int substitutionOffset, boolean
expression, boolean addSemicolon) {
return JavaCompletionItem.createLambdaItem(info, elem, type,
substitutionOffset, expression, addSemicolon);
}
+
+ @Override
+ public JavaCompletionItem createRecordPatternItem(CompilationInfo info,
TypeElement elem, DeclaredType type, int substitutionOffset, ReferencesCount
referencesCount, boolean isDeprecated, boolean insideNew, boolean addTypeVars) {
+ return JavaCompletionItem.createRecordPatternItem(info, elem, type,
substitutionOffset, referencesCount, isDeprecated, insideNew, addTypeVars);
+ }
}
diff --git
a/java/java.hints/src/org/netbeans/modules/java/hints/jdk/ConvertToNestedRecordPattern.java
b/java/java.hints/src/org/netbeans/modules/java/hints/jdk/ConvertToNestedRecordPattern.java
new file mode 100644
index 0000000000..cc2d539e02
--- /dev/null
+++
b/java/java.hints/src/org/netbeans/modules/java/hints/jdk/ConvertToNestedRecordPattern.java
@@ -0,0 +1,278 @@
+/*
+ * 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.jdk;
+
+import com.sun.source.tree.BindingPatternTree;
+import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.DeconstructionPatternTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.ParenthesizedPatternTree;
+import com.sun.source.tree.PatternTree;
+import com.sun.source.tree.StatementTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.tree.CaseTree;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.RecordComponentElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.TreePathHandle;
+import org.netbeans.api.java.source.CodeStyleUtils;
+import org.netbeans.api.java.source.WorkingCopy;
+import org.netbeans.api.java.queries.CompilerOptionsQuery;
+import org.netbeans.api.java.source.support.CancellableTreePathScanner;
+import org.netbeans.modules.java.hints.errors.Utilities;
+import org.netbeans.spi.editor.hints.ErrorDescription;
+import org.netbeans.spi.editor.hints.Fix;
+import org.netbeans.spi.java.hints.ErrorDescriptionFactory;
+import org.netbeans.spi.java.hints.Hint;
+import org.netbeans.spi.java.hints.HintContext;
+import org.netbeans.spi.java.hints.JavaFix;
+import org.netbeans.spi.java.hints.MatcherUtilities;
+import org.netbeans.spi.java.hints.TriggerTreeKind;
+import org.openide.util.NbBundle;
+
[email protected]({
+ "DN_ConvertToNestedRecordPattern=Convert to nested record pattern",
+ "DESC_ConvertToNestedRecordPattern=Convert to nested record pattern",
+ "ERR_ConvertToNestedRecordPattern=Convert to nested record pattern",
+ "FIX_ConvertToNestedRecordPattern=Convert to nested record pattern"
+})
+@Hint(displayName = "#DN_ConvertToNestedRecordPattern", description =
"#DESC_ConvertToNestedRecordPattern", category = "rules15",
+ minSourceVersion = "19")
+/**
+ *
+ * @author mjayan
+ */
+public class ConvertToNestedRecordPattern {
+
+ private static final int RECORD_PATTERN_PREVIEW_JDK_VERSION = 19;
+
+ @TriggerTreeKind(Tree.Kind.DECONSTRUCTION_PATTERN)
+ public static ErrorDescription convertToNestedRecordPattern(HintContext
ctx) {
+ if (Utilities.isJDKVersionLower(RECORD_PATTERN_PREVIEW_JDK_VERSION) &&
!CompilerOptionsQuery.getOptions(ctx.getInfo().getFileObject()).getArguments().contains("--enable-preview"))
{
+ return null;
+ }
+ TreePath t = ctx.getPath();
+ if
(!t.getParentPath().getLeaf().getKind().equals(Tree.Kind.INSTANCE_OF)) {
+ return null;
+ }
+ Set<String> recordPatternVarSet = new HashSet<>();
+ Map<PatternTree, List<PatternTree>> recordComponentMap = new
LinkedHashMap<>();
+ DeconstructionPatternTree recordPattern = (DeconstructionPatternTree)
t.getLeaf();
+ recordComponentMap = findNested(recordPattern, recordComponentMap);
+
+ for (PatternTree p : recordComponentMap.keySet()) {
+ BindingPatternTree bTree = (BindingPatternTree) p;
+ recordPatternVarSet.add(bTree.getVariable().getName().toString());
+ }
+ while (t != null && t.getLeaf().getKind() != Tree.Kind.BLOCK) {
+ t = t.getParentPath();
+ }
+ Set<TreePath> convertPath = new HashSet<>();
+ List<String> localVarList = new ArrayList<>();
+ Map<String, List<UserVariables>> userVars = new HashMap<>();
+ new CancellableTreePathScanner<Void, Void>() {
+
+ @Override
+ public Void visitVariable(VariableTree node, Void p) {
+ localVarList.add(node.getName().toString());
+ Map<String, TreePath> outerVariables = new HashMap<>();
+ Map<String, String> innerVariables = new HashMap<>();
+ List<UserVariables> nList = new ArrayList<>();
+ boolean match = MatcherUtilities.matches(ctx,
getCurrentPath(), "$type $var1 = $expr3.$meth1()", outerVariables, new
HashMap<>(), innerVariables);
+
+ if (match &&
recordPatternVarSet.contains(outerVariables.get("$expr3").getLeaf().toString()))
{
+ String expr3 =
outerVariables.get("$expr3").getLeaf().toString();
+ nList.clear();
+ if (userVars.get(expr3) != null) {
+ nList = userVars.get(expr3);
+ }
+ nList.add(new UserVariables(innerVariables.get("$var1"),
innerVariables.get("$meth1")));
+ userVars.put(expr3, nList);
+ convertPath.add(getCurrentPath());
+ }
+ return super.visitVariable(node, p);
+ }
+
+ @Override
+ protected boolean isCanceled() {
+ return ctx.isCanceled();
+ }
+ }.scan(t, null);
+ if (!convertPath.isEmpty()) {
+ Fix fix = new FixImpl(ctx.getInfo(), ctx.getPath(), convertPath,
localVarList, userVars).toEditorFix();
+ return ErrorDescriptionFactory.forName(ctx, ctx.getPath(),
Bundle.ERR_ConvertToNestedRecordPattern(), fix);
+ }
+ return null;
+ }
+
+ private static Map<PatternTree, List<PatternTree>> findNested(PatternTree
pTree, Map<PatternTree, List<PatternTree>> recordComponentMap) {
+ switch (pTree.getKind()) {
+ case BINDING_PATTERN:
+ recordComponentMap.put(pTree, new ArrayList<>());
+ return recordComponentMap;
+ case DECONSTRUCTION_PATTERN:
+ DeconstructionPatternTree bTree = (DeconstructionPatternTree)
pTree;
+ for (PatternTree p : bTree.getNestedPatterns()) {
+ findNested(p, recordComponentMap);
+ }
+ break;
+ case PARENTHESIZED_PATTERN:
+ ParenthesizedPatternTree parenthTree =
(ParenthesizedPatternTree) pTree;
+ findNested(parenthTree.getPattern(), recordComponentMap);
+ break;
+ default:
+ return recordComponentMap;
+ }
+ return recordComponentMap;
+ }
+
+ private static class UserVariables {
+
+ String methodName;
+ String variable;
+
+ UserVariables(String variable, String methodName) {
+ this.variable = variable;
+ this.methodName = methodName;
+ }
+
+ public String getMethodName() {
+ return methodName;
+ }
+
+ public String getVariable() {
+ return variable;
+ }
+ }
+
+ private static final class FixImpl extends JavaFix {
+
+ private final Map<String, List<UserVariables>> userVars;
+ private final Set<TreePathHandle> replaceOccurrences;
+ List<String> localVarList;
+
+ public FixImpl(CompilationInfo info, TreePath main, Set<TreePath>
replaceOccurrences, List<String> localVarList, Map<String, List<UserVariables>>
userVars) {
+ super(info, main);
+ this.replaceOccurrences = replaceOccurrences.stream().map(tp ->
TreePathHandle.create(tp, info)).collect(Collectors.toSet());
+ this.userVars = userVars;
+ this.localVarList = localVarList;
+ }
+
+ @Override
+ protected String getText() {
+ return Bundle.ERR_ConvertToNestedRecordPattern();
+ }
+
+ @Override
+ protected void performRewrite(JavaFix.TransformationContext ctx) {
+ WorkingCopy wc = ctx.getWorkingCopy();
+ TreePath t = ctx.getPath();
+ TypeElement type = null;
+ Map<PatternTree, List<PatternTree>> recordComponentMap = new
LinkedHashMap<>();
+ DeconstructionPatternTree recordPattern =
(DeconstructionPatternTree) t.getLeaf();
+ recordComponentMap = findNested(recordPattern, recordComponentMap);
+
+ Set<String> localVars = new HashSet<>(localVarList);
+ for (PatternTree p : recordComponentMap.keySet()) {
+ List<PatternTree> bindTree = new ArrayList<>();
+ BindingPatternTree bTree = (BindingPatternTree) p;
+ VariableTree v = bTree.getVariable();
+ type = (TypeElement)
wc.getTrees().getElement(TreePath.getPath(t, v.getType()));
+ if (type == null || type.getRecordComponents().size() == 0) {
+ continue;
+ }
+ outer:
+ for (RecordComponentElement recordComponent :
type.getRecordComponents()) {
+ String name = recordComponent.getSimpleName().toString();
+ TypeMirror returnType =
recordComponent.getAccessor().getReturnType();
+ if (userVars.get(v.getName().toString()) != null) {
+ for (UserVariables var :
userVars.get(v.getName().toString())) {
+ if (var.getMethodName().equals(name)) {
+ bindTree.add((BindingPatternTree)
wc.getTreeMaker().BindingPattern(wc.getTreeMaker().Variable(wc.getTreeMaker().
+
Modifiers(EnumSet.noneOf(Modifier.class)), var.getVariable(),
wc.getTreeMaker().Type(returnType), null)));
+ continue outer;
+ }
+ }
+ }
+ String baseName = name;
+ int cnt = 1;
+ while (SourceVersion.isKeyword(name) ||
localVars.contains(name)) {
+ name = CodeStyleUtils.addPrefixSuffix(baseName +
cnt++, "", "");
+ }
+ localVars.add(name);
+ bindTree.add((BindingPatternTree)
wc.getTreeMaker().BindingPattern(wc.getTreeMaker().Variable(wc.getTreeMaker().
+ Modifiers(EnumSet.noneOf(Modifier.class)), name,
wc.getTreeMaker().Type(returnType), null)));
+ }
+ recordComponentMap.put(p, bindTree);
+ }
+
+ DeconstructionPatternTree d = (DeconstructionPatternTree)
createNestedPattern((PatternTree) t.getLeaf(), wc, recordComponentMap);
+ while (t != null && t.getLeaf().getKind() != Tree.Kind.BLOCK) {
+ t = t.getParentPath();
+ }
+
+ List<Tree> removeList = replaceOccurrences.stream().map(tph ->
tph.resolve(wc).getLeaf()).collect(Collectors.toList());
+ for (Tree tree : removeList) {
+ StatementTree statementTree = (StatementTree) tree;
+ Utilities.removeStatements(wc, TreePath.getPath(t,
statementTree), null);
+ }
+ wc.rewrite(ctx.getPath().getLeaf(), d);
+ }
+ }
+
+ private static PatternTree createNestedPattern(PatternTree pTree,
WorkingCopy wc, Map<PatternTree, List<PatternTree>> map) {
+ switch (pTree.getKind()) {
+ case BINDING_PATTERN:
+ if (map.containsKey(pTree) && !map.get(pTree).isEmpty()) {
+ BindingPatternTree p = (BindingPatternTree) pTree;
+ VariableTree v = (VariableTree) p.getVariable();
+ return (DeconstructionPatternTree)
wc.getTreeMaker().RecordPattern((ExpressionTree) v.getType(), map.get(pTree),
v);
+ } else {
+ return pTree;
+ }
+ case DECONSTRUCTION_PATTERN:
+ DeconstructionPatternTree bTree = (DeconstructionPatternTree)
pTree;
+ List<PatternTree> list = new ArrayList<>();
+ for (PatternTree p : bTree.getNestedPatterns()) {
+ PatternTree val = createNestedPattern(p, wc, map);
+ list.add(val);
+ }
+ return (DeconstructionPatternTree)
wc.getTreeMaker().RecordPattern(bTree.getDeconstructor(), list,
bTree.getVariable());
+ case PARENTHESIZED_PATTERN:
+ ParenthesizedPatternTree parenthTree =
(ParenthesizedPatternTree) pTree;
+ return createNestedPattern(parenthTree.getPattern(), wc, map);
+ default:
+ return pTree;
+ }
+ }
+}
diff --git
a/java/java.hints/src/org/netbeans/modules/java/hints/jdk/ConvertToRecordPattern.java
b/java/java.hints/src/org/netbeans/modules/java/hints/jdk/ConvertToRecordPattern.java
new file mode 100644
index 0000000000..5ead0e6785
--- /dev/null
+++
b/java/java.hints/src/org/netbeans/modules/java/hints/jdk/ConvertToRecordPattern.java
@@ -0,0 +1,194 @@
+/*
+ * 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.jdk;
+
+import com.sun.source.tree.BindingPatternTree;
+import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.IfTree;
+import com.sun.source.tree.InstanceOfTree;
+import com.sun.source.tree.ParenthesizedTree;
+import com.sun.source.tree.PatternTree;
+import com.sun.source.tree.StatementTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+import com.sun.source.tree.VariableTree;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.RecordComponentElement;
+import javax.lang.model.element.TypeElement;
+import org.netbeans.api.java.source.CodeStyleUtils;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.TreePathHandle;
+import org.netbeans.api.java.source.ElementHandle;
+import org.netbeans.api.java.source.WorkingCopy;
+import org.netbeans.api.java.queries.CompilerOptionsQuery;
+import org.netbeans.api.java.source.support.CancellableTreePathScanner;
+import org.netbeans.modules.java.hints.errors.Utilities;
+import org.netbeans.spi.editor.hints.ErrorDescription;
+import org.netbeans.spi.editor.hints.Fix;
+import org.netbeans.spi.java.hints.ErrorDescriptionFactory;
+import org.netbeans.spi.java.hints.Hint;
+import org.netbeans.spi.java.hints.HintContext;
+import org.netbeans.spi.java.hints.JavaFix;
+import org.netbeans.spi.java.hints.MatcherUtilities;
+import org.netbeans.spi.java.hints.TriggerPattern;
+import org.netbeans.spi.java.hints.TriggerPatterns;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author mjayan
+ */
[email protected]({
+ "DN_ConvertToRecordPattern=Convert to instanceof <record pattern>",
+ "DESC_ConvertToRecordPattern=Convert to instanceof <record pattern>",
+ "ERR_ConvertToRecordPattern=instanceof <record pattern> can be used here",
+ "FIX_ConvertToRecordPattern=Use instanceof record pattern"
+})
+@Hint(displayName = "#DN_ConvertToRecordPattern", description =
"#DESC_ConvertToRecordPattern", category = "rules15",
+ minSourceVersion = "19")
+public class ConvertToRecordPattern {
+
+ private static final int RECORD_PATTERN_PREVIEW_JDK_VERSION = 19;
+
+ @TriggerPatterns({
+ @TriggerPattern(value = "if ($expr instanceof $typeI0 $var0 ) {
$statements$;} else $else$;")
+ })
+ public static ErrorDescription trivial(HintContext ctx) {
+ if (Utilities.isJDKVersionLower(RECORD_PATTERN_PREVIEW_JDK_VERSION) &&
!CompilerOptionsQuery.getOptions(ctx.getInfo().getFileObject()).getArguments().contains("--enable-preview"))
{
+ return null;
+ }
+ ElementKind kind =
ctx.getInfo().getTrees().getElement(ctx.getVariables().get("$typeI0")).getKind();
+ if (kind.equals(ElementKind.RECORD)) {
+ Set<TreePath> convertPath = new HashSet<>();
+ Set<String> localVarList = new HashSet<>();
+
localVarList.add(ctx.getInfo().getTrees().getElement(ctx.getVariables().get("$expr")).getSimpleName().toString());
+ Map<String, String> varNames = new HashMap<>();
+ new CancellableTreePathScanner<Void, Void>() {
+ String variableName = null;
+
+ @Override
+ public Void visitVariable(VariableTree node, Void p) {
+ if (variableName == null) {
+ variableName = node.getName().toString();
+ }
+ localVarList.add(node.getName().toString());
+ Map<String, TreePath> outerVariables = new HashMap<>();
+ Map<String, String> innerVariables = new HashMap<>();
+ boolean match = MatcherUtilities.matches(ctx,
getCurrentPath(), "$type $var1 = $expr3.$meth1()", outerVariables, new
HashMap<String, Collection<? extends TreePath>>(), innerVariables);
+
+ if (match &&
outerVariables.get("$expr3").getLeaf().toString().equals(variableName)) {
+ varNames.put(innerVariables.get("$meth1"),
innerVariables.get("$var1"));
+ convertPath.add(getCurrentPath());
+ }
+ return super.visitVariable(node, p);
+ }
+
+ @Override
+ protected boolean isCanceled() {
+ return ctx.isCanceled();
+ }
+ }.scan(ctx.getPath(), null);
+ TypeElement type = (TypeElement)
ctx.getInfo().getTrees().getElement(ctx.getVariables().get("$typeI0"));
+ List<? extends RecordComponentElement> recordSig =
type.getRecordComponents();
+ if (!convertPath.isEmpty()) {
+ Fix fix = new FixImpl(ctx.getInfo(), ctx.getPath(),
convertPath, recordSig, varNames, localVarList).toEditorFix();
+ return ErrorDescriptionFactory.forName(ctx, ctx.getPath(),
Bundle.ERR_ConvertToRecordPattern(), fix);
+ }
+ }
+ return null;
+ }
+
+ private static final class FixImpl extends JavaFix {
+
+ private final Set<TreePathHandle> replaceOccurrences;
+ private final List<? extends ElementHandle> recordSig;
+ private final Map<String, String> varNames;
+ private final Set<String> localVarList;
+
+ public FixImpl(CompilationInfo info, TreePath main, Set<TreePath>
replaceOccurrences, List<? extends RecordComponentElement> recordSig,
Map<String, String> varNames, Set<String> localVarList) {
+ super(info, main);
+ this.recordSig = recordSig.stream().map(elem ->
ElementHandle.create(elem)).collect(Collectors.toList());
+ this.varNames = varNames;
+ this.replaceOccurrences = replaceOccurrences.stream().map(tp ->
TreePathHandle.create(tp, info)).collect(Collectors.toSet());
+ this.localVarList = new HashSet<>(localVarList);
+ }
+
+ @Override
+ protected String getText() {
+ return Bundle.FIX_ConvertToRecordPattern();
+ }
+
+ @Override
+ protected void performRewrite(JavaFix.TransformationContext ctx) {
+ WorkingCopy wc = ctx.getWorkingCopy();
+ TreePath main = ctx.getPath();
+ IfTree it = (IfTree) main.getLeaf();
+ InstanceOfTree iot = (InstanceOfTree) ((ParenthesizedTree)
it.getCondition()).getExpression();
+ BindingPatternTree pattern = (BindingPatternTree) iot.getPattern();
+ StatementTree bt = it.getThenStatement();
+
+ List<PatternTree> bindTree = new ArrayList<>();
+
+ List<RecordComponentElement> recordSignature = new ArrayList<>();
+ recordSig.stream().map(elem -> elem.resolve(wc)).forEach(elem -> {
+ recordSignature.add((RecordComponentElement) elem);
+ });
+ Set<String> localVars = new HashSet<>(localVarList);
+ for (RecordComponentElement recordComponent : recordSignature) {
+ String compName = recordComponent.getSimpleName().toString();
+ String name = null;
+ String returnType = null;
+ if (varNames.containsKey(compName)) {
+ name = varNames.get(compName);
+ } else {
+ int cnt = 1;
+ name = compName;
+ while (SourceVersion.isKeyword(name) ||
localVars.contains(name)) {
+ name = CodeStyleUtils.addPrefixSuffix(compName +
cnt++, "", "");
+ }
+ localVars.add(name);
+ }
+
+ returnType =
recordComponent.getAccessor().getReturnType().toString();
+ returnType = returnType.substring(returnType.lastIndexOf(".")
+ 1);
+ bindTree.add((BindingPatternTree)
wc.getTreeMaker().BindingPattern(wc.getTreeMaker().Variable(wc.getTreeMaker().
+ Modifiers(EnumSet.noneOf(Modifier.class)), name,
wc.getTreeMaker().Identifier(returnType), null)));
+ }
+ InstanceOfTree cond =
wc.getTreeMaker().InstanceOf(iot.getExpression(),
wc.getTreeMaker().RecordPattern((ExpressionTree) pattern.
+ getVariable().getType(), bindTree, pattern.getVariable()));
+ List<Tree> removeList = replaceOccurrences.stream().map(tph ->
tph.resolve(wc).getLeaf()).collect(Collectors.toList());
+ for (Tree t : removeList) {
+ bt = wc.getTreeMaker().removeBlockStatement((BlockTree) bt,
(StatementTree) t);
+ }
+ wc.rewrite(it,
wc.getTreeMaker().If(wc.getTreeMaker().Parenthesized(cond), bt,
it.getElseStatement()));
+ }
+ }
+}
diff --git
a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/ConvertToNestedRecordPatternTest.java
b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/ConvertToNestedRecordPatternTest.java
new file mode 100644
index 0000000000..d985c5929f
--- /dev/null
+++
b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/ConvertToNestedRecordPatternTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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.jdk;
+
+import org.netbeans.junit.NbTestCase;
+import org.netbeans.modules.java.hints.test.api.HintTest;
+import javax.lang.model.SourceVersion;
+
+/**
+ *
+ * @author mjayan
+ */
+public class ConvertToNestedRecordPatternTest extends NbTestCase {
+
+ public ConvertToNestedRecordPatternTest(String name) {
+ super(name);
+ }
+
+ public void testSimple() throws Exception {
+ if (!isRecordClassPresent()) {
+ return;
+ }
+ HintTest.create()
+ .input("package test;\n"
+ + "record Rect(ColoredPoint upperLeft,ColoredPoint lr)
{}\n"
+ + "record ColoredPoint(Point p, Color c) {}\n"
+ + "record Point(int x, int y){}\n"
+ + "enum Color {RED,GREEN,BLUE}\n"
+ + "public class Test {\n"
+ + " private void test(Object o) {\n"
+ + " if (o instanceof Rect(ColoredPoint ul,
ColoredPoint lr)) {\n"
+ + " Point p = ul.p();\n"
+ + " System.out.println(\"Hello\");\n"
+ + " }\n"
+ + " }\n"
+ + "}\n")
+ .sourceLevel(SourceVersion.latest().name())
+ .options("--enable-preview")
+ .run(ConvertToNestedRecordPattern.class)
+ .findWarning("7:25-7:63:verifier:" +
Bundle.ERR_ConvertToNestedRecordPattern())
+ .applyFix()
+ .assertCompilable()
+ .assertOutput("package test;\n"
+ + "record Rect(ColoredPoint upperLeft,ColoredPoint lr)
{}\n"
+ + "record ColoredPoint(Point p, Color c) {}\n"
+ + "record Point(int x, int y){}\n"
+ + "enum Color {RED,GREEN,BLUE}\n"
+ + "public class Test {\n"
+ + " private void test(Object o) {\n"
+ + " if (o instanceof Rect(ColoredPoint(Point p,
Color c) ul, ColoredPoint(Point p1, Color c1) lr)) {\n"
+ + " System.out.println(\"Hello\");\n"
+ + " }\n"
+ + " }\n"
+ + "}\n");
+ }
+
+ public void testMultipleNested() throws Exception {
+ if (!isRecordClassPresent()) {
+ return;
+ }
+ HintTest.create()
+ .input("package test;\n"
+ + "record Rect(ColoredPoint upperLeft) {}\n"
+ + "record ColoredPoint(Point p, Color c) {}\n"
+ + "record Point(int x, int y){}\n"
+ + "enum Color {RED,GREEN,BLUE}\n"
+ + "public class Test {\n"
+ + " private void test(Object o) {\n"
+ + " if (o instanceof Rect(ColoredPoint(Point p,
Color c) ul)) {\n"
+ + " int x = p.x();\n"
+ + " System.out.println(\"Hello\");\n"
+ + " }\n"
+ + " }\n"
+ + "}\n")
+ .sourceLevel(SourceVersion.latest().name())
+ .options("--enable-preview")
+ .run(ConvertToNestedRecordPattern.class)
+ .findWarning("7:25-7:64:verifier:" +
Bundle.ERR_ConvertToNestedRecordPattern())
+ .applyFix()
+ .assertCompilable()
+ .assertOutput("package test;\n"
+ + "record Rect(ColoredPoint upperLeft) {}\n"
+ + "record ColoredPoint(Point p, Color c) {}\n"
+ + "record Point(int x, int y){}\n"
+ + "enum Color {RED,GREEN,BLUE}\n"
+ + "public class Test {\n"
+ + " private void test(Object o) {\n"
+ + " if (o instanceof
Rect(ColoredPoint(Point(int x, int y) p, Color c) ul)) {\n"
+ + " System.out.println(\"Hello\");\n"
+ + " }\n"
+ + " }\n"
+ + "}\n");
+ }
+
+ public void testUserVar() throws Exception {
+ if (!isRecordClassPresent()) {
+ return;
+ }
+ HintTest.create()
+ .input("package test;\n"
+ + "record Rect(ColoredPoint upperLeft,ColoredPoint
lr,ColoredPoint ur) {}\n"
+ + "record ColoredPoint(Point p, Color c) {}\n"
+ + "record Point(int x, int y){}\n"
+ + "enum Color {RED,GREEN,BLUE}\n"
+ + "public class Test {\n"
+ + " private void test(Object o) {\n"
+ + " if (o instanceof Rect(ColoredPoint(Point p,
Color c) ul, ColoredPoint lr, ColoredPoint(Point p1, Color c1) ur)) {\n"
+ + " int xVal = p.x();\n"
+ + " int y1 = p.y();\n"
+ + " Point p2 = lr.p();\n"
+ + " System.out.println(\"Hello\");\n"
+ + " }\n"
+ + " }\n"
+ + "}\n")
+ .sourceLevel(SourceVersion.latest().name())
+ .options("--enable-preview")
+ .run(ConvertToNestedRecordPattern.class)
+ .findWarning("7:25-7:118:verifier:" +
Bundle.ERR_ConvertToNestedRecordPattern())
+ .applyFix()
+ .assertCompilable()
+ .assertOutput("package test;\n"
+ + "record Rect(ColoredPoint upperLeft,ColoredPoint
lr,ColoredPoint ur) {}\n"
+ + "record ColoredPoint(Point p, Color c) {}\n"
+ + "record Point(int x, int y){}\n"
+ + "enum Color {RED,GREEN,BLUE}\n"
+ + "public class Test {\n"
+ + " private void test(Object o) {\n"
+ + " if (o instanceof
Rect(ColoredPoint(Point(int xVal, int y1) p, Color c) ul, ColoredPoint(Point
p2, Color c2) lr, ColoredPoint(Point(int x, int y) p1, Color c1) ur)) {\n"
+ + " System.out.println(\"Hello\");\n"
+ + " }\n"
+ + " }\n"
+ + "}\n");
+ }
+
+ private boolean isRecordClassPresent() {
+ try {
+ Class.forName("java.lang.Record");
+ return true;
+ } catch (ClassNotFoundException ex) {
+ return false;
+ }
+ }
+}
diff --git
a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/ConvertToRecordPatternTest.java
b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/ConvertToRecordPatternTest.java
new file mode 100644
index 0000000000..72cd50c080
--- /dev/null
+++
b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/ConvertToRecordPatternTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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.jdk;
+
+import org.netbeans.junit.NbTestCase;
+import org.netbeans.modules.java.hints.test.api.HintTest;
+import javax.lang.model.SourceVersion;
+
+/**
+ *
+ * @author mjayan
+ */
+public class ConvertToRecordPatternTest extends NbTestCase {
+
+ public ConvertToRecordPatternTest(String name) {
+ super(name);
+ }
+
+ public void testSimple() throws Exception {
+ if (!isRecordClassPresent()) {
+ return;
+ }
+ HintTest.create()
+ .input("package test;\n"
+ + "record Person(String name, String place){}\n"
+ + "public class Test {\n"
+ + " private int test(Object o) {\n"
+ + " if (o instanceof Person p) {\n"
+ + " String name = p.name();\n"
+ + " return name.length();\n"
+ + " }\n"
+ + " return -1;\n"
+ + " }\n"
+ + "}\n")
+ .sourceLevel(SourceVersion.latest().name())
+ .options("--enable-preview")
+ .run(ConvertToRecordPattern.class)
+ .findWarning("4:8-4:10:verifier:" +
Bundle.ERR_ConvertToRecordPattern())
+ .applyFix()
+ .assertCompilable()
+ .assertOutput("package test;\n"
+ + "record Person(String name, String place){}\n"
+ + "public class Test {\n"
+ + " private int test(Object o) {\n"
+ + " if (o instanceof Person(String name, String
place) p) {\n"
+ + " return name.length();\n"
+ + " }\n"
+ + " return -1;\n"
+ + " }\n"
+ + "}\n");
+ }
+
+ public void testDuplicateVarName() throws Exception {
+ if (!isRecordClassPresent()) {
+ return;
+ }
+ HintTest.create()
+ .input("package test;\n"
+ + "record Person(String name, int s){}\n"
+ + "public class Test {\n"
+ + " private int test(Object s) {\n"
+ + " if (s instanceof Person p) {\n"
+ + " String name = p.name();\n"
+ + " return name.length();\n"
+ + " }\n"
+ + " return -1;\n"
+ + " }\n"
+ + "}\n")
+ .sourceLevel(SourceVersion.latest().name())
+ .options("--enable-preview")
+ .run(ConvertToRecordPattern.class)
+ .findWarning("4:8-4:10:verifier:" +
Bundle.ERR_ConvertToRecordPattern())
+ .applyFix()
+ .assertCompilable()
+ .assertOutput("package test;\n"
+ + "record Person(String name, int s){}\n"
+ + "public class Test {\n"
+ + " private int test(Object s) {\n"
+ + " if (s instanceof Person(String name, int
s1) p) {\n"
+ + " return name.length();\n"
+ + " }\n"
+ + " return -1;\n"
+ + " }\n"
+ + "}\n");
+ }
+
+ public void testUsingUserVar() throws Exception {
+ if (!isRecordClassPresent()) {
+ return;
+ }
+ HintTest.create()
+ .input("package test;\n"
+ + "record Person(String name, int s){}\n"
+ + "public class Test {\n"
+ + " private int test(Object s) {\n"
+ + " if (s instanceof Person p) {\n"
+ + " String userName = p.name();\n"
+ + " return userName.length();\n"
+ + " }\n"
+ + " return -1;\n"
+ + " }\n"
+ + "}\n")
+ .sourceLevel(SourceVersion.latest().name())
+ .options("--enable-preview")
+ .run(ConvertToRecordPattern.class)
+ .findWarning("4:8-4:10:verifier:" +
Bundle.ERR_ConvertToRecordPattern())
+ .applyFix()
+ .assertCompilable()
+ .assertOutput("package test;\n"
+ + "record Person(String name, int s){}\n"
+ + "public class Test {\n"
+ + " private int test(Object s) {\n"
+ + " if (s instanceof Person(String userName,
int s1) p) {\n"
+ + " return userName.length();\n"
+ + " }\n"
+ + " return -1;\n"
+ + " }\n"
+ + "}\n");
+ }
+
+ private boolean isRecordClassPresent() {
+ try {
+ Class.forName("java.lang.Record");
+ return true;
+ } catch (ClassNotFoundException ex) {
+ return false;
+ }
+ }
+}
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 cb830c6249..ac708a8fe8 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
@@ -1278,6 +1278,19 @@ public final class TreeMaker {
return delegate.BindingPattern(vt);
}
+ /**
+ * Creates a new Tree for a given DeconstructionPatternTree
+ * @param deconstructor deconstructor of record pattern
+ * @param nested list of nested patterns
+ * @param vt the variable of record pattern
+ * @see com.sun.source.tree.DeconstructionPatternTree
+ * @return the newly created RecordPatternTree
+ * @since 19
+ */
+ public DeconstructionPatternTree RecordPattern(ExpressionTree
deconstructor, List<PatternTree> nested, VariableTree vt) {
+ return delegate.DeconstructionPattern(deconstructor, nested, vt);
+ }
+
/**
* Creates a new VariableTree from a VariableElement.
*
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 46389a0a19..1702191252 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
@@ -60,6 +60,8 @@ public class TreeShims {
// public static final String NULL_LITERAL = "NULL_LITERAL"; //NOI18N
// public static final String PARENTHESIZED_PATTERN =
"PARENTHESIZED_PATTERN"; //NOI18N
// public static final String GUARDED_PATTERN = "GUARDED_PATTERN"; //NOI18N
+// public static final String DECONSTRUCTION_PATTERN =
"DECONSTRUCTION_PATTERN";
+// public static final String RECORDPATTERN = "RECORDPATTERN";
//
// public static List<? extends ExpressionTree> getExpressions(CaseTree
node) {
// try {
@@ -390,6 +392,58 @@ public class TreeShims {
// } catch (IllegalAccessException | IllegalArgumentException |
InvocationTargetException ex) {
// throw TreeShims.<RuntimeException>throwAny(ex);
// }
+// }
+
+// public static ExpressionTree getDeconstructor(Tree node) {
+// try {
+// Class gpt =
Class.forName("com.sun.source.tree.DeconstructionPatternTree"); //NOI18N
+// return isJDKVersionRelease19_Or_Above()
+// ? (ExpressionTree)
gpt.getDeclaredMethod("getDeconstructor").invoke(node) //NOI18N
+// : null;
+// } catch (NoSuchMethodException | ClassNotFoundException ex) {
+// return null;
+// } catch (IllegalAccessException | IllegalArgumentException |
InvocationTargetException ex) {
+// throw TreeShims.<RuntimeException>throwAny(ex);
+// }
+// }
+
+// public static List<? extends PatternTree> getNestedPatterns(Tree node) {
+// try {
+// Class gpt =
Class.forName("com.sun.source.tree.DeconstructionPatternTree"); //NOI18N
+// return isJDKVersionRelease19_Or_Above()
+// ? (List<? extends PatternTree>)
gpt.getDeclaredMethod("getNestedPatterns").invoke(node) //NOI18N
+// : null;
+// } catch (NoSuchMethodException | ClassNotFoundException ex) {
+// return null;
+// } catch (IllegalAccessException | IllegalArgumentException |
InvocationTargetException ex) {
+// throw TreeShims.<RuntimeException>throwAny(ex);
+// }
+// }
+
+// public static VariableTree getVariable(Tree node) {
+// try {
+// Class gpt =
Class.forName("com.sun.source.tree.DeconstructionPatternTree"); //NOI18N
+// return isJDKVersionRelease19_Or_Above()
+// ? (VariableTree)
gpt.getDeclaredMethod("getVariable").invoke(node) //NOI18N
+// : null;
+// } catch (NoSuchMethodException | ClassNotFoundException ex) {
+// return null;
+// } catch (IllegalAccessException | IllegalArgumentException |
InvocationTargetException ex) {
+// throw TreeShims.<RuntimeException>throwAny(ex);
+// }
+// }
+
+// public static Tree RecordPattern(TreeMaker make, ExpressionTree
deconstructor, List<PatternTree> nested, VariableTree var) {
+// ListBuffer<JCTree.JCPattern> nestedVar = new ListBuffer<>();
+// for (PatternTree t : nested) {
+// nestedVar.append((JCTree.JCPattern) t);
+// }
+// try {
+// Method getMethod =
TreeMaker.class.getDeclaredMethod("RecordPattern", JCTree.JCExpression.class,
com.sun.tools.javac.util.List.class, JCTree.JCVariableDecl.class);
+// return (Tree) getMethod.invoke(make, (JCTree.JCExpression)
deconstructor, nestedVar.toList(), (JCTree.JCVariableDecl) var);
+// } catch (NoSuchMethodException | IllegalAccessException |
IllegalArgumentException | InvocationTargetException ex) {
+// throw TreeShims.<RuntimeException>throwAny(ex);
+// }
// }
public static Element toRecordComponent(Element el) {
@@ -425,6 +479,10 @@ public class TreeShims {
return
Integer.valueOf(SourceVersion.latest().name().split("_")[1]).compareTo(17) >= 0;
}
+// public static boolean isJDKVersionRelease19_Or_Above(){
+// return
Integer.valueOf(SourceVersion.latest().name().split("_")[1]).compareTo(19) >= 0;
+// }
+
// public static boolean isJDKVersionRelease18_Or_Above() {
// return
Integer.valueOf(SourceVersion.latest().name().split("_")[1]).compareTo(18) >= 0;
// }
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 5a76b60586..efb1458744 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
@@ -26,6 +26,7 @@ import com.sun.source.tree.LambdaExpressionTree.BodyKind;
import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.ModuleTree;
+import com.sun.source.tree.PatternTree;
import com.sun.source.tree.StatementTree;
import com.sun.source.tree.Tree;
import static com.sun.source.tree.Tree.*;
@@ -2091,8 +2092,26 @@ public final class VeryPretty extends JCTree.Visitor
implements DocTreeVisitor<V
@Override
public void visitTree(JCTree tree) {
- print("(UNKNOWN: " + tree + ")");
- newline();
+ print("(UNKNOWN: " + tree + ")");
+ newline();
+ }
+
+ @Override
+ public void visitRecordPattern(JCRecordPattern tree) {
+ print(tree.deconstructor);
+ print("(");
+ Iterator<JCPattern> it = tree.nested.iterator();
+ while (it.hasNext()) {
+ JCPattern pattern = it.next();
+ doAccept(pattern, true);
+ if (it.hasNext()) {
+ print(", ");
+ }
+ }
+ print(") ");
+ if (tree.var != null) {
+ print(tree.var.name.toString());
+ }
}
/**************************************************************************
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 3bc2d5dd28..f13b66e1ac 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
@@ -105,6 +105,7 @@ import com.sun.tools.javac.tree.JCTree.JCPackageDecl;
import com.sun.tools.javac.tree.JCTree.JCParens;
import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
import com.sun.tools.javac.tree.JCTree.JCProvides;
+import com.sun.tools.javac.tree.JCTree.JCRecordPattern;
import com.sun.tools.javac.tree.JCTree.JCRequires;
import com.sun.tools.javac.tree.JCTree.JCReturn;
import com.sun.tools.javac.tree.JCTree.JCStatement;
@@ -1966,6 +1967,28 @@ public class CasualDiff {
return diffTree((JCTree) oldT.var, (JCTree) newT.var, bounds);
}
+ protected int diffRecordPattern(JCRecordPattern oldT, JCRecordPattern
newT, int[] bounds) {
+ int localPointer = bounds[0];
+
+ // deconstructor
+ int[] exprBounds = getBounds(oldT.deconstructor);
+ copyTo(localPointer, exprBounds[0]);
+ localPointer = diffTree(oldT.deconstructor, newT.deconstructor,
exprBounds);
+
+ // Nested Patterns
+ Iterator<? extends JCTree> oldTIter = oldT.nested.iterator();
+ Iterator<? extends JCTree> newTIter = newT.nested.iterator();
+ while (oldTIter.hasNext()) {
+ JCTree oldP = oldTIter.next();
+ JCTree newP = newTIter.next();
+ int[] patternBounds = getBounds(oldP);
+ copyTo(localPointer, patternBounds[0]);
+ localPointer = diffTree(oldP, newP, patternBounds);
+ }
+ copyTo(localPointer, bounds[1]);
+ return bounds[1];
+ }
+
protected int diffConstantCaseLabel(JCConstantCaseLabel oldT,
JCConstantCaseLabel newT, int[] bounds) {
return diffTree((JCTree) oldT.expr, (JCTree) newT.expr, bounds);
}
@@ -5720,6 +5743,9 @@ public class CasualDiff {
case CONSTANTCASELABEL:
retVal = diffConstantCaseLabel((JCConstantCaseLabel) oldT,
(JCConstantCaseLabel) newT, elementBounds);
break;
+ case RECORDPATTERN:
+ retVal = diffRecordPattern((JCRecordPattern) oldT,
(JCRecordPattern) newT, elementBounds);
+ break;
default:
// handle special cases like field groups and enum constants
if (oldT.getKind() == Kind.OTHER) {
diff --git
a/java/java.source.base/src/org/netbeans/modules/java/source/save/Reformatter.java
b/java/java.source.base/src/org/netbeans/modules/java/source/save/Reformatter.java
index 941da1c039..e04f054f03 100644
---
a/java/java.source.base/src/org/netbeans/modules/java/source/save/Reformatter.java
+++
b/java/java.source.base/src/org/netbeans/modules/java/source/save/Reformatter.java
@@ -2825,8 +2825,10 @@ public class Reformatter implements ReformatTask {
@Override
public Boolean visitDeconstructionPattern(DeconstructionPatternTree
node, Void p) {
scan(node.getDeconstructor(), p);
+ spaces(0);
accept(LPAREN);
- scan(node.getNestedPatterns(), p);
+ spaces(cs.spaceWithinMethodDeclParens() ? 1 : 0, true);
+ wrapList(cs.wrapMethodParams(), cs.alignMultilineMethodParams(),
false, COMMA, node.getNestedPatterns());
accept(RPAREN);
if (node.getVariable() != null) {
space();
diff --git
a/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/save/FormatingTest.java
b/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/save/FormatingTest.java
index a7b245a078..c4afa398dd 100644
---
a/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/save/FormatingTest.java
+++
b/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/save/FormatingTest.java
@@ -3322,6 +3322,183 @@ public class FormatingTest extends NbTestCase {
reformat(doc, content, golden);
}
+ public void testNormalRecordPattern() throws Exception {
+ try {
+ SourceVersion.valueOf("RELEASE_19"); //NOI18N
+ } catch (IllegalArgumentException ex) {
+ return;
+ }
+ testFile = new File(getWorkDir(), "Test.java");
+ TestUtilities.copyStringToFile(testFile, "");
+ FileObject testSourceFO = FileUtil.toFileObject(testFile);
+ DataObject testSourceDO = DataObject.find(testSourceFO);
+ EditorCookie ec = (EditorCookie)
testSourceDO.getCookie(EditorCookie.class);
+ final Document doc = ec.openDocument();
+ doc.putProperty(Language.class, JavaTokenId.language());
+ String content = "package test;\n"
+ + "record Point(int x, int y){}\n"
+ + "public class Test {\n"
+ + " private void test(Object o) {\n"
+ + " if (o instanceof Point\n"
+ + " (int x, int \n"
+ + " y) p) {\n"
+ + " System.out.println(\"Hello\");\n"
+ + " }\n"
+ + " }\n"
+ + "}";
+
+ String golden = "package test;\n"
+ + "\n"
+ + "record Point(int x, int y) {\n"
+ + "\n"
+ + "}\n"
+ + "\n"
+ + "public class Test {\n"
+ + "\n"
+ + " private void test(Object o) {\n"
+ + " if (o instanceof Point(int x, int y) p) {\n"
+ + " System.out.println(\"Hello\");\n"
+ + " }\n"
+ + " }\n"
+ + "}"
+ + "\n";
+ reformat(doc, content, golden);
+ }
+
+ public void testNestedRecordPattern() throws Exception {
+ try {
+ SourceVersion.valueOf("RELEASE_19"); //NOI18N
+ } catch (IllegalArgumentException ex) {
+ //OK, no RELEASE_19, skip test
+ return;
+ }
+ testFile = new File(getWorkDir(), "Test.java");
+ TestUtilities.copyStringToFile(testFile, "");
+ FileObject testSourceFO = FileUtil.toFileObject(testFile);
+ DataObject testSourceDO = DataObject.find(testSourceFO);
+ EditorCookie ec = (EditorCookie)
testSourceDO.getCookie(EditorCookie.class);
+ final Document doc = ec.openDocument();
+ doc.putProperty(Language.class, JavaTokenId.language());
+ String content = "package test;\n"
+ + "\n"
+ + "record Rect(ColoredPoint ul,ColoredPoint lr) {}\n"
+ + "enum Color {RED,GREEN,BLUE}\n"
+ + "record ColoredPoint(Point p, Color c) {}\n"
+ + "record Point(int x, int y) {}\n"
+ + "\n"
+ + "public class Test {\n"
+ + "\n"
+ + " private void test(Object o) {\n"
+ + " if (o instanceof\n"
+ + " Rect\n"
+ + " ( ColoredPoint ul,
ColoredPoint\n"
+ + " lr) r) {\n"
+ + " Point p = ul.p();\n"
+ + " System.out.println(\"Hello\");\n"
+ + " }\n"
+ + " }\n"
+ + "}";
+
+ String golden = "package test;\n"
+ + "\n"
+ + "record Rect(ColoredPoint ul, ColoredPoint lr) {\n"
+ + "\n"
+ + "}\n"
+ + "\n"
+ + "enum Color {\n"
+ + " RED, GREEN, BLUE\n"
+ + "}\n"
+ + "\n"
+ + "record ColoredPoint(Point p, Color c) {\n"
+ + "\n"
+ + "}\n"
+ + "\n"
+ + "record Point(int x, int y) {\n"
+ + "\n"
+ + "}\n"
+ + "\n"
+ + "public class Test {\n"
+ + "\n"
+ + " private void test(Object o) {\n"
+ + " if (o instanceof Rect(ColoredPoint ul, ColoredPoint
lr) r) {\n"
+ + " Point p = ul.p();\n"
+ + " System.out.println(\"Hello\");\n"
+ + " }\n"
+ + " }\n"
+ + "}"
+ + "\n";
+ reformat(doc, content, golden);
+ }
+
+ public void testMultipleNestingRecordPattern() throws Exception {
+ try {
+ SourceVersion.valueOf("RELEASE_19"); //NOI18N
+ } catch (IllegalArgumentException ex) {
+ //OK, no RELEASE_19, skip test
+ return;
+ }
+ testFile = new File(getWorkDir(), "Test.java");
+ TestUtilities.copyStringToFile(testFile, "");
+ FileObject testSourceFO = FileUtil.toFileObject(testFile);
+ DataObject testSourceDO = DataObject.find(testSourceFO);
+ EditorCookie ec = (EditorCookie)
testSourceDO.getCookie(EditorCookie.class);
+ final Document doc = ec.openDocument();
+ doc.putProperty(Language.class, JavaTokenId.language());
+ String content = "package test;\n"
+ + "\n"
+ + "record Rect(ColoredPoint ul,ColoredPoint lr) {}\n"
+ + "enum Color {RED,GREEN,BLUE}\n"
+ + "record ColoredPoint(Point p, Color c) {}\n"
+ + "record Point(int x, int y) {}\n"
+ + "\n"
+ + "public class Test {\n"
+ + "\n"
+ + " private void test(Object o) {\n"
+ + " if (o instanceof\n"
+ + " Rect\n"
+ + " ( ColoredPoint(Point \n"
+ + " p, Color \n"
+ + " c\n"
+ + " ) ul, ColoredPoint\n"
+ + " lr) r\n"
+ + " ) {\n"
+ + " int x = p.x();\n"
+ + " System.out.println(\"Hello\");\n"
+ + " }\n"
+ + " }\n"
+ + "}";
+
+ String golden = "package test;\n"
+ + "\n"
+ + "record Rect(ColoredPoint ul, ColoredPoint lr) {\n"
+ + "\n"
+ + "}\n"
+ + "\n"
+ + "enum Color {\n"
+ + " RED, GREEN, BLUE\n"
+ + "}\n"
+ + "\n"
+ + "record ColoredPoint(Point p, Color c) {\n"
+ + "\n"
+ + "}\n"
+ + "\n"
+ + "record Point(int x, int y) {\n"
+ + "\n"
+ + "}\n"
+ + "\n"
+ + "public class Test {\n"
+ + "\n"
+ + " private void test(Object o) {\n"
+ + " if (o instanceof Rect(ColoredPoint(Point p, Color
c) ul, ColoredPoint lr) r) {\n"
+ + " int x = p.x();\n"
+ + " System.out.println(\"Hello\");\n"
+ + " }\n"
+ + " }\n"
+ + "}"
+ + "\n";
+ reformat(doc, content, golden);
+ }
+
public void testDoWhile() throws Exception {
testFile = new File(getWorkDir(), "Test.java");
TestUtilities.copyStringToFile(testFile,
---------------------------------------------------------------------
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