From 1fad9d24cd3a1e0236ed5415e9efed1c1393a22e Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Thu, 21 Nov 2024 09:34:42 +0100 Subject: [PATCH] Fixing rename for permitted subclasses. --- .../jpa/dao/EjbFacadeWizardIterator.java | 4 +- .../jaxws/nodes/HandlerButtonListener.java | 1 + .../jaxws/nodes/HandlerButtonListener.java | 1 + .../rest/client/ClientJavaSourceHelper.java | 1 + .../websvc/rest/client/OAuthHelper.java | 2 + .../websvc/rest/client/Wadl2JavaHelper.java | 1 + .../codegen/EntityResourcesGenerator.java | 1 + .../websvc/rest/support/JavaSourceHelper.java | 3 +- .../wizard/fromdb/EjbFacadeGenerator.java | 7 +- .../saas/codegen/j2ee/support/J2eeUtil.java | 1 + .../api/support/java/GenerationUtils.java | 1 + .../api/support/java/GenerationUtils.java | 1 + .../modules/java/hints/ClassStructure.java | 2 +- .../java/hints/ConvertAnonymousToInner.java | 2 +- .../modules/java/hints/OrganizeMembers.java | 4 +- .../java/hints/errors/CreateClassFix.java | 11 +- .../java/hints/jdk/AnnotationProcessors.java | 10 +- .../hints/suggestions/CreateSubclass.java | 4 +- .../java/hints/suggestions/Lambda.java | 1 + java/java.source.base/apichanges.xml | 12 + .../nbproject/project.properties | 2 +- .../netbeans/api/java/source/TreeMaker.java | 71 +- .../java/source/builder/TreeFactory.java | 28 +- .../java/source/pretty/VeryPretty.java | 3 +- .../modules/java/source/save/CasualDiff.java | 38 + .../java/source/save/EstimatorFactory.java | 7 + .../java/source/save/PositionEstimator.java | 40 +- .../transform/ImmutableTreeTranslator.java | 7 +- .../java/source/transform/TreeDuplicator.java | 3 +- .../source/usages/SourceAnalyzerFactory.java | 1 + .../MethodTest1/testMethodReturnType.pass | 2 +- .../MethodTest1/testMethodThrows.pass | 2 +- .../api/java/source/gen/ClassMemberTest.java | 37 + .../api/java/source/gen/ClassPermitsTest.java | 795 ++++++++++++++++++ .../source/usages/SourceAnalyzerTest.java | 67 +- .../queriesimpl/TemplateWizardIterator.java | 8 +- .../modules/java/classfile/CodeGenerator.java | 6 +- .../modules/java/ui/FmtCodeGeneration.java | 4 +- .../netbeans/modules/java/ui/FmtNaming.java | 3 +- .../java/classfile/CodeGeneratorTest.java | 24 + .../modules/junit/AbstractTestGenerator.java | 4 + .../modules/junit/JUnit3TestGenerator.java | 3 + .../modules/junit/JUnit4TestGenerator.java | 3 + .../modules/junit/JUnit5TestGenerator.java | 2 + .../netbeans/lib/nbjavac/CheckProcessor.java | 88 ++ .../ExtractInterfaceRefactoringPlugin.java | 3 + .../ExtractSuperclassRefactoringPlugin.java | 2 + .../IntroduceLocalExtensionTransformer.java | 2 + .../java/plugins/MoveClassTransformer.java | 2 + .../java/plugins/PullUpTransformer.java | 2 + .../java/plugins/PushDownTransformer.java | 3 +- .../ReplaceConstructorWithBuilderPlugin.java | 1 + .../java/test/RenameSealedTest.java | 210 +++++ .../modules/java/hints/spiimpl/Utilities.java | 2 +- .../spi/java/hints/JavaFixUtilities.java | 7 +- .../modules/testng/AbstractTestGenerator.java | 4 + .../modules/testng/TestGenerator.java | 2 + 57 files changed, 1492 insertions(+), 66 deletions(-) create mode 100644 java/java.source.base/test/unit/src/org/netbeans/api/java/source/gen/ClassPermitsTest.java create mode 100644 java/lib.nbjavac/src/org/netbeans/lib/nbjavac/CheckProcessor.java create mode 100644 java/refactoring.java/test/unit/src/org/netbeans/modules/refactoring/java/test/RenameSealedTest.java diff --git a/enterprise/j2ee.ejbcore/src/org/netbeans/modules/j2ee/ejbcore/ejb/wizard/jpa/dao/EjbFacadeWizardIterator.java b/enterprise/j2ee.ejbcore/src/org/netbeans/modules/j2ee/ejbcore/ejb/wizard/jpa/dao/EjbFacadeWizardIterator.java index 85d87cf97cb8..a2b16c8707ed 100644 --- a/enterprise/j2ee.ejbcore/src/org/netbeans/modules/j2ee/ejbcore/ejb/wizard/jpa/dao/EjbFacadeWizardIterator.java +++ b/enterprise/j2ee.ejbcore/src/org/netbeans/modules/j2ee/ejbcore/ejb/wizard/jpa/dao/EjbFacadeWizardIterator.java @@ -318,6 +318,7 @@ public void run(WorkingCopy workingCopy) throws Exception { Arrays.asList(maker.TypeParameter(genericsTypeName, Collections.emptyList())), null, Collections.emptyList(), + Collections.emptyList(), members); workingCopy.rewrite(classTree, newClassTree); @@ -452,6 +453,7 @@ public void run(WorkingCopy wc) throws Exception { classTree.getTypeParameters(), extendsClause, implementsClause, + classTree.getPermitsClause(), members); wc.rewrite(classTree, newClassTree); @@ -631,7 +633,7 @@ public void run(WorkingCopy workingCopy) throws Exception { TreeMaker make = workingCopy.getTreeMaker(); AnnotationTree annotations = genUtils.createAnnotation(annotationType); ModifiersTree modifiers = make.Modifiers(clazz.getModifiers(), Collections.singletonList(annotations)); - ClassTree modifiedClass = make.Class(modifiers, clazz.getSimpleName(), clazz.getTypeParameters(), clazz.getExtendsClause(), Collections.emptyList(), Collections.emptyList()); + ClassTree modifiedClass = make.Class(modifiers, clazz.getSimpleName(), clazz.getTypeParameters(), clazz.getExtendsClause(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); workingCopy.rewrite(clazz, modifiedClass); } }); diff --git a/enterprise/maven.jaxws/src/org/netbeans/modules/maven/jaxws/nodes/HandlerButtonListener.java b/enterprise/maven.jaxws/src/org/netbeans/modules/maven/jaxws/nodes/HandlerButtonListener.java index c27d1b7e8853..629b483bb71f 100644 --- a/enterprise/maven.jaxws/src/org/netbeans/modules/maven/jaxws/nodes/HandlerButtonListener.java +++ b/enterprise/maven.jaxws/src/org/netbeans/modules/maven/jaxws/nodes/HandlerButtonListener.java @@ -192,6 +192,7 @@ public void run(WorkingCopy workingCopy) throws IOException { classTree.getTypeParameters(), classTree.getExtendsClause(), classTree.getImplementsClause(), + classTree.getPermitsClause(), classTree.getMembers()); workingCopy.rewrite(classTree, modifiedClass); } diff --git a/enterprise/websvc.core/src/org/netbeans/modules/websvc/core/jaxws/nodes/HandlerButtonListener.java b/enterprise/websvc.core/src/org/netbeans/modules/websvc/core/jaxws/nodes/HandlerButtonListener.java index 433a57dfe7fc..51be1eddc10b 100644 --- a/enterprise/websvc.core/src/org/netbeans/modules/websvc/core/jaxws/nodes/HandlerButtonListener.java +++ b/enterprise/websvc.core/src/org/netbeans/modules/websvc/core/jaxws/nodes/HandlerButtonListener.java @@ -200,6 +200,7 @@ public void run(WorkingCopy workingCopy) throws IOException { classTree.getTypeParameters(), classTree.getExtendsClause(), classTree.getImplementsClause(), + classTree.getPermitsClause(), classTree.getMembers()); workingCopy.rewrite(classTree, modifiedClass); } diff --git a/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/client/ClientJavaSourceHelper.java b/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/client/ClientJavaSourceHelper.java index 1ba76dc16876..23bcc21ef81c 100644 --- a/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/client/ClientJavaSourceHelper.java +++ b/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/client/ClientJavaSourceHelper.java @@ -370,6 +370,7 @@ private static ClassTree addJerseyClientClass ( Collections.emptyList(), null, Collections.emptyList(), + Collections.emptyList(), Collections.emptyList()); ClassTree modifiedInnerClass = diff --git a/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/client/OAuthHelper.java b/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/client/OAuthHelper.java index f60714fffff8..5ace77e6272e 100644 --- a/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/client/OAuthHelper.java +++ b/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/client/OAuthHelper.java @@ -977,6 +977,7 @@ static ClassTree addOAuthServlets(WorkingCopy copy, ClassTree originalClass, Met Collections.emptyList(), extendsTree, Collections.emptyList(), + Collections.emptyList(), Collections.emptyList()); ModifiersTree methodModifiers = maker.Modifiers(Collections.singleton(Modifier.PROTECTED)); @@ -1033,6 +1034,7 @@ static ClassTree addOAuthServlets(WorkingCopy copy, ClassTree originalClass, Met Collections.emptyList(), extendsTree, Collections.emptyList(), + Collections.emptyList(), Collections.emptyList()); methodTree = maker.Method ( diff --git a/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/client/Wadl2JavaHelper.java b/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/client/Wadl2JavaHelper.java index f739dfc6885d..6637c3530415 100644 --- a/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/client/Wadl2JavaHelper.java +++ b/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/client/Wadl2JavaHelper.java @@ -512,6 +512,7 @@ static ClassTree addSessionAuthServlets(WorkingCopy copy, ClassTree originalClas Collections.emptyList(), extendsTree, Collections.emptyList(), + Collections.emptyList(), Collections.emptyList()); ClassTree modifiedInnerClass = innerClass; diff --git a/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/codegen/EntityResourcesGenerator.java b/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/codegen/EntityResourcesGenerator.java index 4b84a583f602..78d23a830ffa 100644 --- a/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/codegen/EntityResourcesGenerator.java +++ b/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/codegen/EntityResourcesGenerator.java @@ -631,6 +631,7 @@ public void run(WorkingCopy workingCopy) throws Exception { classTree.getTypeParameters(), classTree.getExtendsClause(), implementsClause, + classTree.getPermitsClause(), members); workingCopy.rewrite(classTree, newClassTree); diff --git a/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/support/JavaSourceHelper.java b/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/support/JavaSourceHelper.java index 5b4013c7febc..36f445680c6e 100644 --- a/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/support/JavaSourceHelper.java +++ b/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/support/JavaSourceHelper.java @@ -599,7 +599,8 @@ public static ClassTree createInnerClass(WorkingCopy copy, return maker.Class(modifiersTree, className, Collections.emptyList(), createIdentifierTree(copy, classToExtend), - Collections.emptyList(), Collections.emptyList()); + Collections.emptyList(), Collections.emptyList(), + Collections.emptyList()); } public static ClassTree createInnerClass(WorkingCopy copy, diff --git a/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/wizard/fromdb/EjbFacadeGenerator.java b/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/wizard/fromdb/EjbFacadeGenerator.java index 3859bfad4b42..2594abd8dc2c 100644 --- a/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/wizard/fromdb/EjbFacadeGenerator.java +++ b/enterprise/websvc.rest/src/org/netbeans/modules/websvc/rest/wizard/fromdb/EjbFacadeGenerator.java @@ -235,6 +235,7 @@ public void run(WorkingCopy workingCopy) throws Exception { Collections.EMPTY_LIST)), null, Collections.EMPTY_LIST, + Collections.EMPTY_LIST, members); workingCopy.rewrite(classTree, newClassTree); @@ -481,6 +482,7 @@ public void run(WorkingCopy wc) throws Exception { abstractFacadeElement, entityElement.asType())), implementsClause, + classTree.getPermitsClause(), members); wc.rewrite(classTree, newClassTree); @@ -719,8 +721,9 @@ public void run(WorkingCopy workingCopy) throws Exception { ClassTree modifiedClass = make.Class(modifiers, clazz.getSimpleName(), clazz.getTypeParameters(), clazz.getExtendsClause(), - Collections.emptyList(), - Collections.emptyList()); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList()); workingCopy.rewrite(clazz, modifiedClass); } }); diff --git a/enterprise/websvc.saas.codegen.j2ee/src/org/netbeans/modules/websvc/saas/codegen/j2ee/support/J2eeUtil.java b/enterprise/websvc.saas.codegen.j2ee/src/org/netbeans/modules/websvc/saas/codegen/j2ee/support/J2eeUtil.java index d854bb7fd45e..90bde81ea91e 100644 --- a/enterprise/websvc.saas.codegen.j2ee/src/org/netbeans/modules/websvc/saas/codegen/j2ee/support/J2eeUtil.java +++ b/enterprise/websvc.saas.codegen.j2ee/src/org/netbeans/modules/websvc/saas/codegen/j2ee/support/J2eeUtil.java @@ -550,6 +550,7 @@ public void run(WorkingCopy workingCopy) throws Exception { classTree.getTypeParameters(), classTree.getExtendsClause(), classTree.getImplementsClause(), + classTree.getPermitsClause(), classTree.getMembers()); workingCopy.rewrite(classTree, modifiedClass); } diff --git a/enterprise/websvc.utilities/src/org/netbeans/modules/websvc/api/support/java/GenerationUtils.java b/enterprise/websvc.utilities/src/org/netbeans/modules/websvc/api/support/java/GenerationUtils.java index d23cbdfd0e45..510015dd27d7 100644 --- a/enterprise/websvc.utilities/src/org/netbeans/modules/websvc/api/support/java/GenerationUtils.java +++ b/enterprise/websvc.utilities/src/org/netbeans/modules/websvc/api/support/java/GenerationUtils.java @@ -593,6 +593,7 @@ public ClassTree addAnnotation(ClassTree classTree, AnnotationTree annotationTre classTree.getTypeParameters(), classTree.getExtendsClause(), (List)classTree.getImplementsClause(), + classTree.getPermitsClause(), classTree.getMembers()); } diff --git a/java/j2ee.core.utilities/src/org/netbeans/modules/j2ee/core/api/support/java/GenerationUtils.java b/java/j2ee.core.utilities/src/org/netbeans/modules/j2ee/core/api/support/java/GenerationUtils.java index ceba94631146..c892c3f615b5 100644 --- a/java/j2ee.core.utilities/src/org/netbeans/modules/j2ee/core/api/support/java/GenerationUtils.java +++ b/java/j2ee.core.utilities/src/org/netbeans/modules/j2ee/core/api/support/java/GenerationUtils.java @@ -598,6 +598,7 @@ public ClassTree addAnnotation(ClassTree classTree, AnnotationTree annotationTre classTree.getTypeParameters(), classTree.getExtendsClause(), (List)classTree.getImplementsClause(), + classTree.getPermitsClause(), classTree.getMembers()); } diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/ClassStructure.java b/java/java.hints/src/org/netbeans/modules/java/hints/ClassStructure.java index 28e7aeb19597..dc3099f27ac9 100644 --- a/java/java.hints/src/org/netbeans/modules/java/hints/ClassStructure.java +++ b/java/java.hints/src/org/netbeans/modules/java/hints/ClassStructure.java @@ -292,7 +292,7 @@ protected void performRewrite(TransformationContext ctx) { gu.copyComments(mods, nmods, false); mods = nmods; } - Tree nue = treeMaker.Interface(mods, cls.getSimpleName(), cls.getTypeParameters(), cls.getImplementsClause(), cls.getMembers()); + Tree nue = treeMaker.Interface(mods, cls.getSimpleName(), cls.getTypeParameters(), cls.getImplementsClause(), cls.getPermitsClause(), cls.getMembers()); gu.copyComments(cls, nue, true); gu.copyComments(cls, nue, false); wc.rewrite(path.getLeaf(), nue); diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/ConvertAnonymousToInner.java b/java/java.hints/src/org/netbeans/modules/java/hints/ConvertAnonymousToInner.java index 264fa790e890..7ac0d4ba2004 100644 --- a/java/java.hints/src/org/netbeans/modules/java/hints/ConvertAnonymousToInner.java +++ b/java/java.hints/src/org/netbeans/modules/java/hints/ConvertAnonymousToInner.java @@ -531,7 +531,7 @@ static void convertAnonymousToInner(WorkingCopy copy, TreePath newClassToConvert String newClassName = generateName(copy, newClassToConvert, superTypeElement.getSimpleName().toString()); - ClassTree clazz = make.Class(classModifiers, newClassName, Collections.emptyList(), superTypeElement.getKind().isClass() ? superTypeTree : null, superTypeElement.getKind().isClass() ? Collections.emptyList() : Collections.singletonList(superTypeTree), members); + ClassTree clazz = make.Class(classModifiers, newClassName, Collections.emptyList(), superTypeElement.getKind().isClass() ? superTypeTree : null, superTypeElement.getKind().isClass() ? Collections.emptyList() : Collections.singletonList(superTypeTree), Collections.emptyList(), members); copy.rewrite(target, make.addClassMember(target, copy.getTreeMaker().asReplacementOf(clazz, nct))); diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/OrganizeMembers.java b/java/java.hints/src/org/netbeans/modules/java/hints/OrganizeMembers.java index 78714c3e02a7..26639d0d9f4c 100644 --- a/java/java.hints/src/org/netbeans/modules/java/hints/OrganizeMembers.java +++ b/java/java.hints/src/org/netbeans/modules/java/hints/OrganizeMembers.java @@ -122,7 +122,7 @@ private static void doOrganizeMembers(WorkingCopy copy, TreePath path) { ClassTree clazz = (ClassTree) path.getLeaf(); clazz = gu.importComments(clazz, copy.getCompilationUnit()); TreeMaker maker = copy.getTreeMaker(); - ClassTree nue = maker.Class(clazz.getModifiers(), clazz.getSimpleName(), clazz.getTypeParameters(), clazz.getExtendsClause(), clazz.getImplementsClause(), Collections.emptyList()); + ClassTree nue = maker.Class(clazz.getModifiers(), clazz.getSimpleName(), clazz.getTypeParameters(), clazz.getExtendsClause(), clazz.getImplementsClause(), clazz.getPermitsClause(), Collections.emptyList()); List members = new ArrayList<>(clazz.getMembers().size()); Map memberMap = new HashMap<>(clazz.getMembers().size()); @@ -170,7 +170,7 @@ private static void doOrganizeMembers(WorkingCopy copy, TreePath path) { nue = GeneratorUtilities.get(copy).insertClassMembers(nue, members); } // now create a new class, based on the original one - retain the order decided by GeneratorUtilities. - ClassTree changed = maker.Class(clazz.getModifiers(), clazz.getSimpleName(), clazz.getTypeParameters(), clazz.getExtendsClause(), clazz.getImplementsClause(), Collections.emptyList()); + ClassTree changed = maker.Class(clazz.getModifiers(), clazz.getSimpleName(), clazz.getTypeParameters(), clazz.getExtendsClause(), clazz.getImplementsClause(), clazz.getPermitsClause(), Collections.emptyList()); int index = 0; for (Tree t : nue.getMembers()) { Tree orig = memberMap.get(t); diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/errors/CreateClassFix.java b/java/java.hints/src/org/netbeans/modules/java/hints/errors/CreateClassFix.java index d1c08fbb3949..669aba154b2f 100644 --- a/java/java.hints/src/org/netbeans/modules/java/hints/errors/CreateClassFix.java +++ b/java/java.hints/src/org/netbeans/modules/java/hints/errors/CreateClassFix.java @@ -204,9 +204,9 @@ protected ClassTree createConstructor(WorkingCopy working, TreePath targetTreePa switch (kind) { case CLASS: - return make.Class(nueModifiers, targetTree.getSimpleName(), typeParameters, extendsClause, implementsClause, targetTree.getMembers()); + return make.Class(nueModifiers, targetTree.getSimpleName(), typeParameters, extendsClause, implementsClause, targetTree.getPermitsClause(), targetTree.getMembers()); case INTERFACE: - return make.Interface(nueModifiers, targetTree.getSimpleName(), typeParameters, implementsClause, targetTree.getMembers()); + return make.Interface(nueModifiers, targetTree.getSimpleName(), typeParameters, implementsClause, targetTree.getPermitsClause(), targetTree.getMembers()); case ANNOTATION_TYPE: return make.AnnotationType(nueModifiers, targetTree.getSimpleName(), targetTree.getMembers()); case ENUM: @@ -353,10 +353,10 @@ public void run(final WorkingCopy working) throws IOException { ClassTree source; switch (kind) { case CLASS: - source = make.Class(nueModifiers, simpleName, typeParameters, extendsClause, implementsClause, members); + source = make.Class(nueModifiers, simpleName, typeParameters, extendsClause, implementsClause, Collections.emptyList(), members); break; case INTERFACE: - source = make.Interface(nueModifiers, simpleName, typeParameters, implementsClause, members); + source = make.Interface(nueModifiers, simpleName, typeParameters, implementsClause, Collections.emptyList(), members); break; case ANNOTATION_TYPE: source = make.AnnotationType(nueModifiers, simpleName, members); @@ -404,6 +404,7 @@ public void run(WorkingCopy parameter) throws Exception { Collections.emptyList(), null, Collections.emptyList(), + Collections.emptyList(), Collections.emptyList()); ClassTree nue = createConstructor(parameter, new TreePath(new TreePath(cut), source)); @@ -513,7 +514,7 @@ public void run(final WorkingCopy working) throws IOException { TreeMaker make = working.getTreeMaker(); MethodTree constr = make.Method(make.Modifiers(EnumSet.of(Modifier.PUBLIC)), "", null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), "{}" /*XXX*/, null); // NOI18N - ClassTree innerClass = make.Class(make.Modifiers(modifiers), name, Collections.emptyList(), null, Collections.emptyList(), Collections.singletonList(constr)); + ClassTree innerClass = make.Class(make.Modifiers(modifiers), name, Collections.emptyList(), null, Collections.emptyList(), Collections.emptyList(), Collections.singletonList(constr)); innerClass = createConstructor(working, new TreePath(targetTree, innerClass)); diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/jdk/AnnotationProcessors.java b/java/java.hints/src/org/netbeans/modules/java/hints/jdk/AnnotationProcessors.java index 97b880bf7392..b01990844baf 100644 --- a/java/java.hints/src/org/netbeans/modules/java/hints/jdk/AnnotationProcessors.java +++ b/java/java.hints/src/org/netbeans/modules/java/hints/jdk/AnnotationProcessors.java @@ -382,14 +382,8 @@ public void makeGetSupportedOverride(WorkingCopy wc, SourceVersion projectSource List annos = new ArrayList<>(nct.getModifiers().getAnnotations()); if (annos.remove(tp.getLeaf())) { make.asRemoved(tp.getLeaf()); - nct = make.Class( - make.Modifiers(nct.getModifiers(), annos), - nct.getSimpleName().toString(), - nct.getTypeParameters(), - nct.getExtendsClause(), - nct.getImplementsClause(), - nct.getMembers() - ); + wc.rewrite(nct.getModifiers(), + make.Modifiers(nct.getModifiers(), annos)); } } } diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/suggestions/CreateSubclass.java b/java/java.hints/src/org/netbeans/modules/java/hints/suggestions/CreateSubclass.java index 1b4ec422da9d..9adc621379e8 100644 --- a/java/java.hints/src/org/netbeans/modules/java/hints/suggestions/CreateSubclass.java +++ b/java/java.hints/src/org/netbeans/modules/java/hints/suggestions/CreateSubclass.java @@ -247,7 +247,7 @@ public void run(WorkingCopy parameter) throws Exception { return; } } - parameter.rewrite(source, make.Class(source.getModifiers(), simpleName, typeParameters, make.Type(superTypeElement.asType()), source.getImplementsClause(), source.getMembers())); + parameter.rewrite(source, make.Class(source.getModifiers(), simpleName, typeParameters, make.Type(superTypeElement.asType()), source.getImplementsClause(), source.getPermitsClause(), source.getMembers())); for (ExecutableElement ctor : ElementFilter.constructorsIn(superTypeElement.getEnclosedElements())) { if (!ctor.getParameters().isEmpty()) { hasNonDefaultConstructor = true; @@ -259,7 +259,7 @@ public void run(WorkingCopy parameter) throws Exception { List newImpls = new ArrayList(impls.size() + 1); newImpls.addAll(impls); newImpls.add(make.Type(superTypeElement.asType())); - parameter.rewrite(source, make.Class(source.getModifiers(), source.getSimpleName(), typeParameters, source.getExtendsClause(), newImpls, source.getMembers())); + parameter.rewrite(source, make.Class(source.getModifiers(), source.getSimpleName(), typeParameters, source.getExtendsClause(), newImpls, source.getPermitsClause(), source.getMembers())); } if (parameter.getFileObject() == null) { parameter.rewrite(null, cut); diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/suggestions/Lambda.java b/java/java.hints/src/org/netbeans/modules/java/hints/suggestions/Lambda.java index d1fdd2527b07..ce0869e1b17f 100644 --- a/java/java.hints/src/org/netbeans/modules/java/hints/suggestions/Lambda.java +++ b/java/java.hints/src/org/netbeans/modules/java/hints/suggestions/Lambda.java @@ -402,6 +402,7 @@ protected void performRewrite(TransformationContext ctx) throws Exception { Collections.emptyList(), null, Collections.emptyList(), + Collections.emptyList(), Collections.singletonList(newMethod)); ExpressionTree targetTypeTree; diff --git a/java/java.source.base/apichanges.xml b/java/java.source.base/apichanges.xml index 236e7eca85bc..3129518683d2 100644 --- a/java/java.source.base/apichanges.xml +++ b/java/java.source.base/apichanges.xml @@ -25,6 +25,18 @@ Java Source API + + + Adding TreeMaker.Class overload that takes permitted subclasses + + + + + + Adding TreeMaker.Class overload that allows to specify the permitted subclasses. + + + SourceUtils.classNameFor nested classes support diff --git a/java/java.source.base/nbproject/project.properties b/java/java.source.base/nbproject/project.properties index 13d64fc1f9bc..8e3b53a4a147 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.75.0 +spec.version.base=2.76.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 2b443de9ba75..06404889eff9 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 @@ -357,15 +357,68 @@ public CatchTree Catch(VariableTree parameter, BlockTree block) { * @param memberDecls the list of fields defined by this class, or an * empty list. * @see com.sun.source.tree.ClassTree + * @deprecated use the method overload that also accepts the list of permitted types */ + @Deprecated public ClassTree Class(ModifiersTree modifiers, CharSequence simpleName, List typeParameters, Tree extendsClause, List implementsClauses, List memberDecls) { - return delegate.Class(modifiers, simpleName, typeParameters, extendsClause, implementsClauses, memberDecls); + return Class(modifiers, simpleName, typeParameters, extendsClause, implementsClauses, List.of(), memberDecls); + } + + /** + * Creates a new ClassTree. + * + * @param modifiers the modifiers declaration + * @param simpleName the name of the class without its package, such + * as "String" for the class "java.lang.String". + * @param typeParameters the list of type parameters, or an empty list. + * @param extendsClause the name of the class this class extends, or null. + * @param implementsClauses the list of the interfaces this class + * implements, or an empty list. + * @param permitsClauses the list of the subtype this class + * permits, or an empty list. + * @param memberDecls the list of fields defined by this class, or an + * empty list. + * @see com.sun.source.tree.ClassTree + * @since 2.76 + */ + public ClassTree Class(ModifiersTree modifiers, + CharSequence simpleName, + List typeParameters, + Tree extendsClause, + List implementsClauses, + List permitsClauses, + List memberDecls) { + return delegate.Class(modifiers, simpleName, typeParameters, extendsClause, implementsClauses, permitsClauses, memberDecls); } + + /** + * Creates a new ClassTree representing interface. + * + * @param modifiers the modifiers declaration + * @param simpleName the name of the class without its package, such + * as "String" for the class "java.lang.String". + * @param typeParameters the list of type parameters, or an empty list. + * @param extendsClauses the list of the interfaces this class + * extends, or an empty list. + * @param memberDecls the list of fields defined by this class, or an + * empty list. + * @see com.sun.source.tree.ClassTree + * @deprecated use the method overload that also accepts the list of permitted types + */ + @Deprecated + public ClassTree Interface(ModifiersTree modifiers, + CharSequence simpleName, + List typeParameters, + List extendsClauses, + List memberDecls) { + return Interface(modifiers, simpleName, typeParameters, extendsClauses, List.of(), memberDecls); + } + /** * Creates a new ClassTree representing interface. * @@ -375,16 +428,20 @@ public ClassTree Class(ModifiersTree modifiers, * @param typeParameters the list of type parameters, or an empty list. * @param extendsClauses the list of the interfaces this class * extends, or an empty list. + * @param permitsClauses the list of the subtype this class + * permits, or an empty list. * @param memberDecls the list of fields defined by this class, or an * empty list. * @see com.sun.source.tree.ClassTree + * @since 2.76 */ public ClassTree Interface(ModifiersTree modifiers, CharSequence simpleName, List typeParameters, List extendsClauses, + List permitsClauses, List memberDecls) { - return delegate.Interface(modifiers, simpleName, typeParameters, extendsClauses, memberDecls); + return delegate.Interface(modifiers, simpleName, typeParameters, extendsClauses, permitsClauses, memberDecls); } /** @@ -2442,8 +2499,10 @@ public ModifiersTree addModifiersModifier(ModifiersTree modifiers, Modifier modi case TRANSIENT: c = c | Flags.TRANSIENT; break; case VOLATILE: c = c | Flags.VOLATILE; break; case DEFAULT: c = c | Flags.DEFAULT; break; + case SEALED: c = c | Flags.SEALED; break; + case NON_SEALED: c = c | Flags.NON_SEALED; break; default: - break; + throw new IllegalStateException("Unsupported modifier: " + modifier); } return Modifiers(c, modifiers.getAnnotations()); } @@ -2463,8 +2522,10 @@ public ModifiersTree removeModifiersModifier(ModifiersTree modifiers, Modifier m case TRANSIENT: c = c & ~Flags.TRANSIENT; break; case VOLATILE: c = c & ~Flags.VOLATILE; break; case DEFAULT: c = c & ~Flags.DEFAULT; break; + case SEALED: c = c & ~Flags.SEALED; break; + case NON_SEALED: c = c & ~Flags.NON_SEALED; break; default: - break; + throw new IllegalStateException("Unsupported modifier: " + modifier); } return Modifiers(c, modifiers.getAnnotations()); } @@ -3024,6 +3085,7 @@ private N setLabelImpl(final N node, final CharSequence aLabel) t.getTypeParameters(), t.getExtendsClause(), (List) t.getImplementsClause(), + t.getPermitsClause(), membersCopy); return clone; } @@ -3151,6 +3213,7 @@ public ClassTree setExtends(final ClassTree node, final ExpressionTree extendz) node.getTypeParameters(), extendz, (List) node.getImplementsClause(), // bug + node.getPermitsClause(), node.getMembers() ); return result; 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 abb39db21f52..de05690d5f2c 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 @@ -279,6 +279,7 @@ public ClassTree Class(ModifiersTree modifiers, List typeParameters, Tree extendsClause, List implementsClauses, + List permitsClauses, List memberDecls) { ListBuffer typarams = new ListBuffer(); @@ -287,6 +288,9 @@ public ClassTree Class(ModifiersTree modifiers, ListBuffer impls = new ListBuffer(); for (Tree t : implementsClauses) impls.append((JCExpression)t); + ListBuffer permits = new ListBuffer(); + for (Tree t : permitsClauses) + permits.append((JCExpression)t); ListBuffer defs = new ListBuffer(); for (Tree t : memberDecls) defs.append((JCTree)t); @@ -295,6 +299,7 @@ public ClassTree Class(ModifiersTree modifiers, typarams.toList(), (JCExpression)extendsClause, impls.toList(), + permits.toList(), defs.toList()); } @@ -303,17 +308,18 @@ public ClassTree Interface(ModifiersTree modifiers, CharSequence simpleName, List typeParameters, List extendsClauses, + List permitsClauses, List memberDecls) { long flags = getBitFlags(modifiers.getFlags()) | Flags.INTERFACE; - return Class(flags, (com.sun.tools.javac.util.List) modifiers.getAnnotations(), simpleName, typeParameters, null, extendsClauses, memberDecls); + return Class(flags, (com.sun.tools.javac.util.List) modifiers.getAnnotations(), simpleName, typeParameters, null, extendsClauses, permitsClauses, memberDecls); } public ClassTree AnnotationType(ModifiersTree modifiers, CharSequence simpleName, List memberDecls) { long flags = getBitFlags(modifiers.getFlags()) | Flags.ANNOTATION; - return Class(flags, (com.sun.tools.javac.util.List) modifiers.getAnnotations(), simpleName, Collections.emptyList(), null, Collections.emptyList(), memberDecls); + return Class(flags, (com.sun.tools.javac.util.List) modifiers.getAnnotations(), simpleName, Collections.emptyList(), null, Collections.emptyList(), Collections.emptyList(), memberDecls); } public ClassTree Enum(ModifiersTree modifiers, @@ -321,7 +327,7 @@ public ClassTree Enum(ModifiersTree modifiers, List implementsClauses, List memberDecls) { long flags = getBitFlags(modifiers.getFlags()) | Flags.ENUM; - return Class(flags, (com.sun.tools.javac.util.List) modifiers.getAnnotations(), simpleName, Collections.emptyList(), null, implementsClauses, memberDecls); + return Class(flags, (com.sun.tools.javac.util.List) modifiers.getAnnotations(), simpleName, Collections.emptyList(), null, implementsClauses, Collections.emptyList(), memberDecls); } public CompilationUnitTree CompilationUnit(PackageTree packageDecl, @@ -650,6 +656,8 @@ public static long modifiersToFlags(Set flagset) { case NATIVE: flags |= Flags.NATIVE; break; case STRICTFP: flags |= Flags.STRICTFP; break; case DEFAULT: flags |= Flags.DEFAULT; break; + case SEALED: flags |= Flags.SEALED; break; + case NON_SEALED: flags |= Flags.NON_SEALED; break; default: throw new AssertionError("Unknown Modifier: " + mod); //NOI18N } @@ -1105,6 +1113,7 @@ private ClassTree modifyClassMember(ClassTree clazz, int index, Tree member, Ope clazz.getTypeParameters(), clazz.getExtendsClause(), (List) clazz.getImplementsClause(), + clazz.getPermitsClause(), c(clazz.getMembers(), index, member, op) ); return copy; @@ -1133,6 +1142,7 @@ private ClassTree modifyClassTypeParameter(ClassTree clazz, int index, TypeParam c(clazz.getTypeParameters(), index, typeParameter, op), clazz.getExtendsClause(), (List) clazz.getImplementsClause(), + clazz.getPermitsClause(), clazz.getMembers() ); return copy; @@ -1161,6 +1171,7 @@ private ClassTree modifyClassImplementsClause(ClassTree clazz, int index, Tree i clazz.getTypeParameters(), clazz.getExtendsClause(), c((List) clazz.getImplementsClause(), index, implementsClause, op), // todo: cast! + clazz.getPermitsClause(), clazz.getMembers() ); return copy; @@ -1754,6 +1765,7 @@ private ClassTree Class(long modifiers, List typeParameters, Tree extendsClause, List implementsClauses, + List permitsClauses, List memberDecls) { ListBuffer typarams = new ListBuffer(); for (TypeParameterTree t : typeParameters) @@ -1761,6 +1773,9 @@ private ClassTree Class(long modifiers, ListBuffer impls = new ListBuffer(); for (Tree t : implementsClauses) impls.append((JCExpression)t); + ListBuffer permits = new ListBuffer(); + for (Tree t : permitsClauses) + permits.append((JCExpression)t); ListBuffer defs = new ListBuffer(); for (Tree t : memberDecls) defs.append((JCTree)t); @@ -1769,12 +1784,13 @@ private ClassTree Class(long modifiers, typarams.toList(), (JCExpression)extendsClause, impls.toList(), + permits.toList(), defs.toList()); } private long getBitFlags(Set modifiers) { - int flags = 0; + long flags = 0; for (Modifier modifier : modifiers) { switch (modifier) { case PUBLIC: flags |= PUBLIC; break; @@ -1788,8 +1804,10 @@ private long getBitFlags(Set modifiers) { case SYNCHRONIZED: flags |= SYNCHRONIZED; break; case NATIVE: flags |= NATIVE; break; case STRICTFP: flags |= STRICTFP; break; + case SEALED: flags |= SEALED; break; + case NON_SEALED: flags |= NON_SEALED; break; default: - break; + throw new IllegalStateException("Unknown modifier: " + modifier); } } return flags; 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 559d428ebb64..210452c8c07a 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 @@ -2882,7 +2882,7 @@ public void printFinallyBlock(JCBlock finalizer) { public void printFlags(long flags, boolean addSpace) { print(flagNames(flags & ~INTERFACE & ~ANNOTATION & ~ENUM)); - if ((flags & StandardFlags) != 0) { + if ((flags & (StandardFlags | Flags.SEALED | Flags.NON_SEALED)) != 0) { if (cs.placeNewLineAfterModifiers()) toColExactly(out.leftMargin); else if (addSpace) @@ -2896,6 +2896,7 @@ else if (addSpace) for (Flag flag : Flag.values()) { flagLowerCaseNames[flag.ordinal()] = flag.name().toLowerCase(Locale.ENGLISH); } + flagLowerCaseNames[Flag.NON_SEALED.ordinal()] = "non-sealed"; } /** 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 037eb8251775..c37a410404cb 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 @@ -1008,6 +1008,10 @@ protected int diffClassDef(JCClassDecl oldT, JCClassDecl newT, int[] bounds) { moveToSrcRelevant(tokenSequence, Direction.FORWARD); // it can be > (GT) or >> (SHIFT) insertHint = tokenSequence.offset() + tokenSequence.token().length(); + if (newT.typarams.isEmpty()) { + //skip the final '>': + localPointer = insertHint; + } } //TODO: class to record and vice versa! if (oldT.getKind() == Kind.RECORD && newT.getKind() == Kind.RECORD) { @@ -1071,6 +1075,7 @@ protected int diffClassDef(JCClassDecl oldT, JCClassDecl newT, int[] bounds) { localPointer = endPos(oldT.extending); break; } + { // TODO (#pf): there is some space for optimization. If the new list // is also empty, we can skip this computation. if (oldT.implementing.isEmpty()) { @@ -1098,6 +1103,39 @@ protected int diffClassDef(JCClassDecl oldT, JCClassDecl newT, int[] bounds) { if (!newT.implementing.isEmpty()) copyTo(localPointer, insertHint); localPointer = diffList2(oldT.implementing, newT.implementing, insertHint, estimator); + } + + { + // TODO (#pf): there is some space for optimization. If the new list + // is also empty, we can skip this computation. + if (oldT.permitting.isEmpty()) { + // if there is not any permits part, we need to adjust position + // from different place. Look at the examples in all if branches. + // | represents current adjustment and ! where we want to point to + if (oldT.implementing.nonEmpty()) { + // public class Yerba| implements Runnable! { ... + insertHint = endPos(oldT.implementing); + } else if (oldT.extending != null) + // public class Yerba| extends Object! { ... + insertHint = endPos(oldT.extending); + else { + // currently no need to adjust anything here: + // public class Yerba|! { ... + } + } else { + // we already have any permits, adjust position to first + // public class Yerba| permits !Mate { ... + // Note: in case of all permits classes are removed, + // diffing mechanism will solve the permits keyword. + insertHint = oldT.permitting.iterator().next().getStartPosition(); + } + PositionEstimator estimator = + EstimatorFactory.permits(oldT.getPermitsClause(), newT.getPermitsClause(), diffContext); + if (!newT.permitting.isEmpty()) + copyTo(localPointer, insertHint); + localPointer = diffList2(oldT.permitting, newT.permitting, insertHint, estimator); + } + insertHint = endPos(oldT) - 1; if (filteredOldTDefs.isEmpty()) { diff --git a/java/java.source.base/src/org/netbeans/modules/java/source/save/EstimatorFactory.java b/java/java.source.base/src/org/netbeans/modules/java/source/save/EstimatorFactory.java index 5a2de8080233..bf2e16c51f66 100644 --- a/java/java.source.base/src/org/netbeans/modules/java/source/save/EstimatorFactory.java +++ b/java/java.source.base/src/org/netbeans/modules/java/source/save/EstimatorFactory.java @@ -82,6 +82,13 @@ static PositionEstimator extendz(List oldL, return new PositionEstimator.ExtendsEstimator(oldL, newL, diffContext); } + static PositionEstimator permits(List oldL, + List newL, + DiffContext diffContext) + { + return new PositionEstimator.PermitsEstimator(oldL, newL, diffContext); + } + static PositionEstimator statements(List oldL, List newL, DiffContext diffContext) diff --git a/java/java.source.base/src/org/netbeans/modules/java/source/save/PositionEstimator.java b/java/java.source.base/src/org/netbeans/modules/java/source/save/PositionEstimator.java index 5ee0dcd97900..8a8a9ffc070e 100644 --- a/java/java.source.base/src/org/netbeans/modules/java/source/save/PositionEstimator.java +++ b/java/java.source.base/src/org/netbeans/modules/java/source/save/PositionEstimator.java @@ -36,6 +36,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.function.Predicate; import javax.swing.text.StyledDocument; import org.netbeans.api.editor.guards.GuardedSection; import org.netbeans.api.editor.guards.GuardedSectionManager; @@ -168,6 +169,18 @@ static class ExtendsEstimator extends BaseEstimator { } } + static class PermitsEstimator extends BaseEstimator { + private static final java.lang.String CONSTANT = "permits"; + PermitsEstimator(List oldL, + List newL, + DiffContext diffContext) + { + super(token -> token.id() != JavaTokenId.IDENTIFIER && + !CONSTANT.contentEquals(token.text()), + CONSTANT, oldL, newL, diffContext); + } + } + static class ThrowsEstimator extends BaseEstimator { ThrowsEstimator(List oldL, List newL, @@ -187,7 +200,7 @@ static class CasePatternEstimator extends BaseEstimator { @Override public String head() { - return precToken.fixedText() + " "; + return prefixTokenText + " "; } } @@ -202,7 +215,7 @@ static class StringTemaplateEstimator extends BaseEstimator { @Override public String head() { - return precToken.fixedText(); + return prefixTokenText; } @Override @@ -814,19 +827,31 @@ public int[] sectionRemovalBounds(StringBuilder replacement) { private abstract static class BaseEstimator extends PositionEstimator { - JavaTokenId precToken; + Predicate> prefixTokenAcceptor; + String prefixTokenText; private ArrayList separatorList; private BaseEstimator(JavaTokenId precToken, List oldL, List newL, DiffContext diffContext) + { + this(token -> token.id() != precToken, precToken.fixedText(), + oldL, newL, diffContext); + } + + private BaseEstimator(Predicate> prefixTokenAcceptor, + String prefixTokenText, + List oldL, + List newL, + DiffContext diffContext) { super(oldL, newL, diffContext); - this.precToken = precToken; + this.prefixTokenAcceptor = prefixTokenAcceptor; + this.prefixTokenText = prefixTokenText; } - public String head() { return " " + precToken.fixedText() + " "; } + public String head() { return " " + prefixTokenText + " "; } public String sep() { return ", "; } @SuppressWarnings("empty-statement") @@ -848,7 +873,7 @@ public void initialize() { int beforer = -1; if (first) { // go back to throws keywrd. - while (seq.movePrevious() && seq.token().id() != precToken) ; + while (seq.movePrevious() && prefixTokenAcceptor.test(seq.token())) ; int throwsIndex = seq.index(); beforer = throwsIndex+1; // go back to closing ) @@ -868,7 +893,8 @@ else if (seq.token().id() == LINE_COMMENT) separatedText = '\n' + separatedText; separatorList.add(separatedText); int separator = seq.index(); - int afterSeparator = separator + 1; // bug + while (seq.moveNext() && seq.token().id() == WHITESPACE); + int afterSeparator = seq.index(); if (afterPrevious == separator) { afterPrevious = -1; } 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 4efeb9b2af6c..f8e1ba0a5a17 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 @@ -686,15 +686,18 @@ protected final ClassTree rewriteChildren(ClassTree tree) { Tree extending = translateClassRef(tree.getExtendsClause()); List implementing = translateClassRef((List)tree.getImplementsClause()); + List permits = + translateClassRef((List)tree.getPermitsClause()); importAnalysis.enterVisibleThroughClasses(tree); List defs = translate(tree.getMembers()); boolean typeChanged = !typarams.equals(tree.getTypeParameters()) || extending != tree.getExtendsClause() || - !implementing.equals(tree.getImplementsClause()); + !implementing.equals(tree.getImplementsClause()) || + !permits.equals(tree.getPermitsClause()); if (typeChanged || mods != tree.getModifiers() || !defs.equals(tree.getMembers())) { ClassTree n = make.Class(mods, tree.getSimpleName(), typarams, - extending, implementing, defs); + extending, implementing, permits, defs); if (!typeChanged) { model.setElement(n, model.getElement(tree)); model.setType(n, model.getType(tree)); diff --git a/java/java.source.base/src/org/netbeans/modules/java/source/transform/TreeDuplicator.java b/java/java.source.base/src/org/netbeans/modules/java/source/transform/TreeDuplicator.java index c09091ec46d1..d516505f6d70 100644 --- a/java/java.source.base/src/org/netbeans/modules/java/source/transform/TreeDuplicator.java +++ b/java/java.source.base/src/org/netbeans/modules/java/source/transform/TreeDuplicator.java @@ -215,7 +215,8 @@ public Tree visitCatch(CatchTree tree, Void p) { @Override public Tree visitClass(ClassTree tree, Void p) { ClassTree n = make.Class(tree.getModifiers(), tree.getSimpleName(), tree.getTypeParameters(), - tree.getExtendsClause(), tree.getImplementsClause(), tree.getMembers()); + tree.getExtendsClause(), tree.getImplementsClause(), + tree.getPermitsClause(), tree.getMembers()); model.setElement(n, model.getElement(tree)); model.setType(n, model.getType(tree)); comments.copyComments(tree, n); diff --git a/java/java.source.base/src/org/netbeans/modules/java/source/usages/SourceAnalyzerFactory.java b/java/java.source.base/src/org/netbeans/modules/java/source/usages/SourceAnalyzerFactory.java index ba099217f152..8d0bf5a4fde6 100644 --- a/java/java.source.base/src/org/netbeans/modules/java/source/usages/SourceAnalyzerFactory.java +++ b/java/java.source.base/src/org/netbeans/modules/java/source/usages/SourceAnalyzerFactory.java @@ -718,6 +718,7 @@ public Void visitClass (@NonNull final ClassTree node, @NonNull final Map {\n" + + " public Test() {\n" + + " }\n" + + " public static void m(String[] args) {\n" + + " }\n" + + "}\n" + ); + String golden = + "package hierbas.del.litoral;\n" + + "\n" + + "public class Test {\n" + + " public Test() {\n" + + " }\n" + + "}\n"; + + JavaSource src = getJavaSource(testFile); + Task task = new Task() { + + public void run(WorkingCopy workingCopy) throws Exception { + workingCopy.toPhase(Phase.RESOLVED); // is it neccessary? + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + workingCopy.rewrite(clazz, make.removeClassMember(clazz, 1)); + } + }; + src.runModificationTask(task).commit(); + String res = TestUtilities.copyFileToString(testFile); + //System.err.println(res); + assertEquals(golden, res); + } + String getGoldenPckg() { return ""; } diff --git a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/gen/ClassPermitsTest.java b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/gen/ClassPermitsTest.java new file mode 100644 index 000000000000..7f60e86a2c71 --- /dev/null +++ b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/gen/ClassPermitsTest.java @@ -0,0 +1,795 @@ +/* + * 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.ClassTree; +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.ModifiersTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.VariableTree; +import java.io.File; +import java.io.IOException; +import java.util.*; +import javax.lang.model.element.Modifier; +import javax.lang.model.type.TypeKind; +import org.netbeans.api.java.source.Task; +import org.netbeans.api.java.source.JavaSource; +import org.netbeans.api.java.source.TestUtilities; +import org.netbeans.api.java.source.TreeMaker; +import static org.netbeans.api.java.source.JavaSource.*; +import org.netbeans.api.java.source.WorkingCopy; +import org.netbeans.junit.NbTestSuite; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; + +/** + * Test adding/removing/modifying permits clause in source. + * In addition to, tries to work with extends in interfaces. + */ +public class ClassPermitsTest extends GeneratorTestMDRCompat { + + /** Creates a new instance of ClassExtendsTest */ + public ClassPermitsTest(String testName) { + super(testName); + } + + public static NbTestSuite suite() { + NbTestSuite suite = new NbTestSuite(); + suite.addTestSuite(ClassPermitsTest.class); + return suite; + } + + public void testModifyExistingPermits1() throws Exception { + testFile = new File(getWorkDir(), "Test.java"); + TestUtilities.copyStringToFile(testFile, + """ + package hierbas.del.litoral; + + public sealed class Test permits Subtype1 { + } + final class Subtype1 extends Test {} + final class Subtype2 extends Test {} + """ + ); + String golden = + """ + package hierbas.del.litoral; + + public sealed class Test permits Subtype2 { + } + final class Subtype1 extends Test {} + final class Subtype2 extends Test {} + """; + JavaSource src = getJavaSource(testFile); + + Task task = new Task() { + + public void run(WorkingCopy workingCopy) throws IOException { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + workingCopy.rewrite(clazz.getPermitsClause().get(0), + make.setLabel(clazz.getPermitsClause().get(0), "Subtype2")); + } + + }; + src.runModificationTask(task).commit(); + String res = TestUtilities.copyFileToString(testFile); + //System.err.println(res); + assertEquals(golden, res); + } + + public void testModifyExistingPermits2() throws Exception { + testFile = new File(getWorkDir(), "Test.java"); + String code = """ + package hierbas.del.litoral; + + public sealed class Test permits Subtype2 { + } + final class Subtype1 extends Test {} + final class Subtype2 extends Test {} + final class Subtype3 extends Test {} + """; + TestUtilities.copyStringToFile(testFile, code); + + JavaSource src = getJavaSource(testFile); + + Task task; + String res; + + //add first: + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + List augmentedPermits = new ArrayList<>(clazz.getPermitsClause()); + augmentedPermits.add(0, make.QualIdent("hierbas.del.litoral.Subtype1")); + ClassTree newClass = make.Class(clazz.getModifiers(), clazz.getSimpleName(), clazz.getTypeParameters(), clazz.getExtendsClause(), clazz.getImplementsClause(), augmentedPermits, clazz.getMembers()); + workingCopy.rewrite(clazz, newClass); + }; + src.runModificationTask(task).commit(); + res = TestUtilities.copyFileToString(testFile); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public sealed class Test permits Subtype1, Subtype2 { + } + final class Subtype1 extends Test {} + final class Subtype2 extends Test {} + final class Subtype3 extends Test {} + """, res); + + //remove first: + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + List augmentedPermits = new ArrayList<>(clazz.getPermitsClause()); + augmentedPermits.remove(0); + ClassTree newClass = make.Class(clazz.getModifiers(), clazz.getSimpleName(), clazz.getTypeParameters(), clazz.getExtendsClause(), clazz.getImplementsClause(), augmentedPermits, clazz.getMembers()); + workingCopy.rewrite(clazz, newClass); + }; + src.runModificationTask(task).commit(); + res = TestUtilities.copyFileToString(testFile); + //System.err.println(res); + assertEquals(code, res); + + //add last: + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + List augmentedPermits = new ArrayList<>(clazz.getPermitsClause()); + augmentedPermits.add(make.QualIdent("hierbas.del.litoral.Subtype3")); + ClassTree newClass = make.Class(clazz.getModifiers(), clazz.getSimpleName(), clazz.getTypeParameters(), clazz.getExtendsClause(), clazz.getImplementsClause(), augmentedPermits, clazz.getMembers()); + workingCopy.rewrite(clazz, newClass); + }; + src.runModificationTask(task).commit(); + res = TestUtilities.copyFileToString(testFile); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public sealed class Test permits Subtype2, Subtype3 { + } + final class Subtype1 extends Test {} + final class Subtype2 extends Test {} + final class Subtype3 extends Test {} + """, res); + + //remove last: + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + List augmentedPermits = new ArrayList<>(clazz.getPermitsClause()); + augmentedPermits.remove(1); + ClassTree newClass = make.Class(clazz.getModifiers(), clazz.getSimpleName(), clazz.getTypeParameters(), clazz.getExtendsClause(), clazz.getImplementsClause(), augmentedPermits, clazz.getMembers()); + workingCopy.rewrite(clazz, newClass); + }; + src.runModificationTask(task).commit(); + res = TestUtilities.copyFileToString(testFile); + //System.err.println(res); + assertEquals(code, res); + } + + public void testIntroduceRemovePermits() throws Exception { + testFile = new File(getWorkDir(), "Test.java"); + String code = """ + package hierbas.del.litoral; + + public sealed class Test { + } + final class Subtype1 extends Test {} + final class Subtype2 extends Test {} + final class Subtype3 extends Test {} + """; + TestUtilities.copyStringToFile(testFile, code); + + JavaSource src = getJavaSource(testFile); + + Task task; + String res; + + //add first: + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + List augmentedPermits = new ArrayList<>(); + augmentedPermits.add(make.QualIdent("hierbas.del.litoral.Subtype1")); + ClassTree newClass = make.Class(clazz.getModifiers(), clazz.getSimpleName(), clazz.getTypeParameters(), clazz.getExtendsClause(), clazz.getImplementsClause(), augmentedPermits, clazz.getMembers()); + workingCopy.rewrite(clazz, newClass); + }; + src.runModificationTask(task).commit(); + res = TestUtilities.copyFileToString(testFile); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public sealed class Test permits Subtype1 { + } + final class Subtype1 extends Test {} + final class Subtype2 extends Test {} + final class Subtype3 extends Test {} + """, res); + + //remove first: + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + List augmentedPermits = new ArrayList<>(clazz.getPermitsClause()); + augmentedPermits.remove(0); + ClassTree newClass = make.Class(clazz.getModifiers(), clazz.getSimpleName(), clazz.getTypeParameters(), clazz.getExtendsClause(), clazz.getImplementsClause(), augmentedPermits, clazz.getMembers()); + workingCopy.rewrite(clazz, newClass); + }; + src.runModificationTask(task).commit(); + res = TestUtilities.copyFileToString(testFile); + //System.err.println(res); + assertEquals(code, res); + } + + public void testClassModification() throws Exception { + testFile = new File(getWorkDir(), "Test.java"); + String code; + code = """ + package hierbas.del.litoral; + + public sealed class Test permits Subtype1 { + } + final class Subtype1 extends Test {} + """; + TestUtilities.copyStringToFile(testFile, code); + + JavaSource src = getJavaSource(testFile); + FileObject testFileFO = FileUtil.toFileObject(testFile); + + Task task; + String res; + + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + ClassTree newClass = make.setLabel(clazz, "Test2"); + workingCopy.rewrite(clazz, newClass); + }; + res = src.runModificationTask(task).getResultingSource(testFileFO); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public sealed class Test2 permits Subtype1 { + } + final class Subtype1 extends Test {} + """, + res); + + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + ClassTree newClass = make.setExtends(clazz, make.Identifier("Object")); + workingCopy.rewrite(clazz, newClass); + }; + res = src.runModificationTask(task).getResultingSource(testFileFO); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public sealed class Test extends Object permits Subtype1 { + } + final class Subtype1 extends Test {} + """, + res); + + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + ClassTree newClass = make.addClassImplementsClause(clazz, make.Identifier("Runnable")); + workingCopy.rewrite(clazz, newClass); + }; + res = src.runModificationTask(task).getResultingSource(testFileFO); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public sealed class Test implements Runnable permits Subtype1 { + } + final class Subtype1 extends Test {} + """, + res); + + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + ClassTree newClass = make.addClassTypeParameter(clazz, make.TypeParameter("T", List.of())); + workingCopy.rewrite(clazz, newClass); + }; + res = src.runModificationTask(task).getResultingSource(testFileFO); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public sealed class Test permits Subtype1 { + } + final class Subtype1 extends Test {} + """, + res); + + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + VariableTree var = make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), "I", make.PrimitiveType(TypeKind.INT), null); + ClassTree newClass = make.addClassMember(clazz, var); + workingCopy.rewrite(clazz, newClass); + }; + res = src.runModificationTask(task).getResultingSource(testFileFO); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public sealed class Test permits Subtype1 { + + int I; + } + final class Subtype1 extends Test {} + """, + res); + + code = """ + package hierbas.del.litoral; + + public sealed class Test implements Runnable permits Subtype1 { + } + final class Subtype1 extends Test {} + """; + TestUtilities.copyStringToFile(testFileFO, code); + + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + ClassTree newClass = make.removeClassImplementsClause(clazz, 0); + workingCopy.rewrite(clazz, newClass); + }; + res = src.runModificationTask(task).getResultingSource(testFileFO); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public sealed class Test permits Subtype1 { + } + final class Subtype1 extends Test {} + """, + res); + + code = """ + package hierbas.del.litoral; + + public sealed class Test permits Subtype1 { + } + final class Subtype1 extends Test {} + """; + TestUtilities.copyStringToFile(testFileFO, code); + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + ClassTree newClass = make.removeClassTypeParameter(clazz, 0); + workingCopy.rewrite(clazz, newClass); + }; + res = src.runModificationTask(task).getResultingSource(testFileFO); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public sealed class Test permits Subtype1 { + } + final class Subtype1 extends Test {} + """, + res); + + code = """ + package hierbas.del.litoral; + + public sealed class Test permits Subtype1 { + int I; + } + final class Subtype1 extends Test {} + """; + TestUtilities.copyStringToFile(testFileFO, code); + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + ClassTree newClass = make.removeClassMember(clazz, 1); + workingCopy.rewrite(clazz, newClass); + }; + res = src.runModificationTask(task).getResultingSource(testFileFO); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public sealed class Test permits Subtype1 { + } + final class Subtype1 extends Test {} + """, + res); + } + + public void testInterfaceModification() throws Exception { + testFile = new File(getWorkDir(), "Test.java"); + String code; + code = """ + package hierbas.del.litoral; + + public sealed interface Test permits Subtype1 { + } + final class Subtype1 extends Test {} + """; + TestUtilities.copyStringToFile(testFile, code); + + JavaSource src = getJavaSource(testFile); + FileObject testFileFO = FileUtil.toFileObject(testFile); + + Task task; + String res; + + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + ClassTree newClass = make.setLabel(clazz, "Test2"); + workingCopy.rewrite(clazz, newClass); + }; + res = src.runModificationTask(task).getResultingSource(testFileFO); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public sealed interface Test2 permits Subtype1 { + } + final class Subtype1 extends Test {} + """, + res); + + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + ClassTree newClass = make.setExtends(clazz, make.Identifier("Object")); + workingCopy.rewrite(clazz, newClass); + }; + res = src.runModificationTask(task).getResultingSource(testFileFO); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public sealed interface Test extends Object permits Subtype1 { + } + final class Subtype1 extends Test {} + """, + res); + + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + ClassTree newClass = make.addClassImplementsClause(clazz, make.Identifier("Runnable")); + workingCopy.rewrite(clazz, newClass); + }; + res = src.runModificationTask(task).getResultingSource(testFileFO); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public sealed interface Test extends Runnable permits Subtype1 { + } + final class Subtype1 extends Test {} + """, + res); + + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + ClassTree newClass = make.addClassTypeParameter(clazz, make.TypeParameter("T", List.of())); + workingCopy.rewrite(clazz, newClass); + }; + res = src.runModificationTask(task).getResultingSource(testFileFO); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public sealed interface Test permits Subtype1 { + } + final class Subtype1 extends Test {} + """, + res); + + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + VariableTree var = make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), "I", make.PrimitiveType(TypeKind.INT), null); + ClassTree newClass = make.addClassMember(clazz, var); + workingCopy.rewrite(clazz, newClass); + }; + res = src.runModificationTask(task).getResultingSource(testFileFO); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public sealed interface Test permits Subtype1 { + + int I; + } + final class Subtype1 extends Test {} + """, + res); + + code = """ + package hierbas.del.litoral; + + public sealed interface Test extends Runnable permits Subtype1 { + } + final class Subtype1 extends Test {} + """; + TestUtilities.copyStringToFile(testFileFO, code); + + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + ClassTree newClass = make.removeClassImplementsClause(clazz, 0); + workingCopy.rewrite(clazz, newClass); + }; + res = src.runModificationTask(task).getResultingSource(testFileFO); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public sealed interface Test permits Subtype1 { + } + final class Subtype1 extends Test {} + """, + res); + + code = """ + package hierbas.del.litoral; + + public sealed interface Test permits Subtype1 { + } + final class Subtype1 extends Test {} + """; + TestUtilities.copyStringToFile(testFileFO, code); + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + ClassTree newClass = make.removeClassTypeParameter(clazz, 0); + workingCopy.rewrite(clazz, newClass); + }; + res = src.runModificationTask(task).getResultingSource(testFileFO); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public sealed interface Test permits Subtype1 { + } + final class Subtype1 extends Test {} + """, + res); + + code = """ + package hierbas.del.litoral; + + public sealed interface Test permits Subtype1 { + int I; + } + final class Subtype1 extends Test {} + """; + TestUtilities.copyStringToFile(testFileFO, code); + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + ClassTree newClass = make.removeClassMember(clazz, 0); + workingCopy.rewrite(clazz, newClass); + }; + res = src.runModificationTask(task).getResultingSource(testFileFO); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public sealed interface Test permits Subtype1 { + } + final class Subtype1 extends Test {} + """, + res); + } + + public void testAddRemovedSealed() throws Exception { + testFile = new File(getWorkDir(), "Test.java"); + String code = """ + package hierbas.del.litoral; + + public class Test { + } + final class Subtype extends Test {} + """; + TestUtilities.copyStringToFile(testFile, code); + + JavaSource src = getJavaSource(testFile); + + Task task; + String res; + + //add sealed: + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + List augmentedPermits = new ArrayList<>(); + augmentedPermits.add(make.QualIdent("hierbas.del.litoral.Subtype")); + ModifiersTree mods = clazz.getModifiers(); + mods = make.addModifiersModifier(mods, Modifier.SEALED); + ClassTree newClass = make.Class(mods, clazz.getSimpleName(), clazz.getTypeParameters(), clazz.getExtendsClause(), clazz.getImplementsClause(), augmentedPermits, clazz.getMembers()); + workingCopy.rewrite(clazz, newClass); + }; + src.runModificationTask(task).commit(); + res = TestUtilities.copyFileToString(testFile); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public sealed class Test permits Subtype { + } + final class Subtype extends Test {} + """, res); + + //remove sealed: + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + List augmentedPermits = new ArrayList<>(clazz.getPermitsClause()); + augmentedPermits.remove(0); + ModifiersTree mods = clazz.getModifiers(); + mods = make.removeModifiersModifier(mods, Modifier.SEALED); + ClassTree newClass = make.Class(mods, clazz.getSimpleName(), clazz.getTypeParameters(), clazz.getExtendsClause(), clazz.getImplementsClause(), augmentedPermits, clazz.getMembers()); + workingCopy.rewrite(clazz, newClass); + }; + src.runModificationTask(task).commit(); + res = TestUtilities.copyFileToString(testFile); + //System.err.println(res); + assertEquals(code, res); + + //add non-sealed: + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + ModifiersTree mods = clazz.getModifiers(); + mods = make.addModifiersModifier(mods, Modifier.NON_SEALED); + ClassTree newClass = make.Class(mods, clazz.getSimpleName(), clazz.getTypeParameters(), clazz.getExtendsClause(), clazz.getImplementsClause(), clazz.getPermitsClause(), clazz.getMembers()); + workingCopy.rewrite(clazz, newClass); + }; + src.runModificationTask(task).commit(); + res = TestUtilities.copyFileToString(testFile); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public non-sealed class Test { + } + final class Subtype extends Test {} + """, res); + + //remove sealed: + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + ModifiersTree mods = clazz.getModifiers(); + mods = make.removeModifiersModifier(mods, Modifier.NON_SEALED); + ClassTree newClass = make.Class(mods, clazz.getSimpleName(), clazz.getTypeParameters(), clazz.getExtendsClause(), clazz.getImplementsClause(), clazz.getPermitsClause(), clazz.getMembers()); + workingCopy.rewrite(clazz, newClass); + }; + src.runModificationTask(task).commit(); + res = TestUtilities.copyFileToString(testFile); + //System.err.println(res); + assertEquals(code, res); + + //TODO: syntheticze sealed/non-sealed: + task = (WorkingCopy workingCopy) -> { + workingCopy.toPhase(Phase.RESOLVED); + CompilationUnitTree cut = workingCopy.getCompilationUnit(); + TreeMaker make = workingCopy.getTreeMaker(); + ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); + List newMembers = new ArrayList<>(clazz.getMembers()); + newMembers.add(make.Interface(make.Modifiers(Set.of(Modifier.SEALED)), "Sealed", List.of(), List.of(), List.of(), List.of())); + newMembers.add(make.Interface(make.Modifiers(Set.of(Modifier.NON_SEALED)), "NonSealed", List.of(), List.of(), List.of(), List.of())); + ClassTree newClass = make.Class(clazz.getModifiers(), clazz.getSimpleName(), clazz.getTypeParameters(), clazz.getExtendsClause(), clazz.getImplementsClause(), clazz.getPermitsClause(), newMembers); + workingCopy.rewrite(clazz, newClass); + }; + src.runModificationTask(task).commit(); + res = TestUtilities.copyFileToString(testFile); + //System.err.println(res); + assertEquals(""" + package hierbas.del.litoral; + + public class Test { + + sealed interface Sealed { + } + + non-sealed interface NonSealed { + } + } + final class Subtype extends Test {} + """, res); + } + + String getGoldenPckg() { + return ""; + } + + String getSourcePckg() { + return ""; + } + + @Override + String getSourceLevel() { + return "17"; + } +} diff --git a/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/usages/SourceAnalyzerTest.java b/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/usages/SourceAnalyzerTest.java index 42d56fc861af..097f31ba8491 100644 --- a/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/usages/SourceAnalyzerTest.java +++ b/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/usages/SourceAnalyzerTest.java @@ -231,6 +231,63 @@ public void report(Diagnostic diagnostic) { } } + public void testPermittedSubclasses() throws Exception { + final FileObject javaFile = FileUtil.toFileObject(TestFileUtils.writeFile( + new File(FileUtil.toFile(src),"Test.java"), //NOI18N + "public class Test permits Foo { \n" + //NOI18N + " public static void main(String[] args) {\n" + //NOI18N + " }\n" + //NOI18N + "}\n" + //NOI18N + "class Foo extends Test {\n" + //NOI18N + "}")); //NOI18N + + final DiagnosticListener diag = new DiagnosticListener() { + + private final Queue> problems = new ArrayDeque>(); + + @Override + public void report(Diagnostic diagnostic) { + problems.offer(diagnostic); + } + }; + SLQ slq = Lookup.getDefault().lookup(SLQ.class); + String prevSourceLevel = slq.setSourceLevel("17"); + TransactionContext.beginStandardTransaction(src.toURL(), true, ()->false, true); + try { + final ClasspathInfo cpInfo = ClasspathInfoAccessor.getINSTANCE().create( + src, + null, + true, + true, + false, + true); + final JavaFileObject jfo = FileObjects.sourceFileObject(javaFile, src); + final JavacTaskImpl jt = JavacParser.createJavacTask( + cpInfo, + diag, + SourceLevelQuery.getSourceLevel(src), //NOI18N + SourceLevelQuery.Profile.DEFAULT, + null, null, null, null, Arrays.asList(jfo)); + final Iterable trees = jt.parse(); + jt.enter(); + jt.analyze(); + final SourceAnalyzerFactory.SimpleAnalyzer sa = SourceAnalyzerFactory.createSimpleAnalyzer(); + List, Object[]>> data = sa.analyseUnit(trees.iterator().next(), jt); + assertEquals(2, data.size()); + Pair, java.lang.Object[]> testRecord = + data.stream() + .filter(p -> p.first().first().getBinaryName().equals("Test")) + .findAny() + .orElseThrow(); + assertTrue(((Collection)testRecord.second()[0]).contains( + DocumentUtil.encodeUsage("Foo", EnumSet.of( //NOI18N + ClassIndexImpl.UsageType.TYPE_REFERENCE)))); + } finally { + TransactionContext.get().rollBack(); + slq.setSourceLevel(prevSourceLevel); + } + } + public static final class CPP implements ClassPathProvider { @@ -255,7 +312,7 @@ public ClassPath findClassPath(FileObject file, String type) { public static final class SLQ implements SourceLevelQueryImplementation2 { - private static final Result2 R = new Result2() { + private final Result2 R = new Result2() { @Override public SourceLevelQuery.Profile getProfile() { @@ -263,7 +320,7 @@ public SourceLevelQuery.Profile getProfile() { } @Override public String getSourceLevel() { - return "1.8"; //NOI18N + return sourceLevel; //NOI18N } @Override public void addChangeListener(ChangeListener listener) { @@ -274,6 +331,7 @@ public void removeChangeListener(ChangeListener listener) { }; private volatile FileObject root; + private volatile String sourceLevel = "1.8"; @Override public Result getSourceLevel(FileObject javaFile) { @@ -284,7 +342,12 @@ public Result getSourceLevel(FileObject javaFile) { return null; } + public String setSourceLevel(String newSourceLevel) { + String prev = sourceLevel; + sourceLevel = newSourceLevel; + return prev; + } } } diff --git a/java/java.source.queriesimpl/src/org/netbeans/modules/java/source/queriesimpl/TemplateWizardIterator.java b/java/java.source.queriesimpl/src/org/netbeans/modules/java/source/queriesimpl/TemplateWizardIterator.java index 476a50335aab..0b7237f3c3b0 100644 --- a/java/java.source.queriesimpl/src/org/netbeans/modules/java/source/queriesimpl/TemplateWizardIterator.java +++ b/java/java.source.queriesimpl/src/org/netbeans/modules/java/source/queriesimpl/TemplateWizardIterator.java @@ -135,13 +135,7 @@ public void run(WorkingCopy wcopy) throws Exception { ExpressionTree extendsTree = superclassElm != null ? maker.QualIdent(superclassElm) : maker.Identifier(superclassName); - ClassTree copy = maker.Class( - orig.getModifiers(), - orig.getSimpleName(), - orig.getTypeParameters(), - extendsTree, - orig.getImplementsClause(), - orig.getMembers()); + ClassTree copy = maker.setExtends(orig, extendsTree); wcopy.rewrite(orig, copy); break; } diff --git a/java/java.source/src/org/netbeans/modules/java/classfile/CodeGenerator.java b/java/java.source/src/org/netbeans/modules/java/classfile/CodeGenerator.java index db4d16d9d7fb..fe6bd7441446 100644 --- a/java/java.source/src/org/netbeans/modules/java/classfile/CodeGenerator.java +++ b/java/java.source/src/org/netbeans/modules/java/classfile/CodeGenerator.java @@ -406,14 +406,14 @@ public Tree visitType(TypeElement e, Void p) { switch (e.getKind()) { case CLASS: - return addDeprecated(e, make.Class(mods, e.getSimpleName(), constructTypeParams(e.getTypeParameters()), computeSuper(e.getSuperclass()), computeSuper(e.getInterfaces()), members)); + return addDeprecated(e, make.Class(mods, e.getSimpleName(), constructTypeParams(e.getTypeParameters()), computeSuper(e.getSuperclass()), computeSuper(e.getInterfaces()), computeSuper(e.getPermittedSubclasses()), members)); case INTERFACE: - return addDeprecated(e, make.Interface(mods, e.getSimpleName(), constructTypeParams(e.getTypeParameters()), computeSuper(e.getInterfaces()), members)); + return addDeprecated(e, make.Interface(mods, e.getSimpleName(), constructTypeParams(e.getTypeParameters()), computeSuper(e.getInterfaces()), computeSuper(e.getPermittedSubclasses()), members)); case ENUM: return addDeprecated(e, make.Enum(mods, e.getSimpleName(), computeSuper(e.getInterfaces()), members)); case RECORD: // TODO generates final class atm - return addDeprecated(e, make.Class(mods, e.getSimpleName(), constructTypeParams(e.getTypeParameters()), null, computeSuper(e.getInterfaces()), members)); + return addDeprecated(e, make.Class(mods, e.getSimpleName(), constructTypeParams(e.getTypeParameters()), null, computeSuper(e.getInterfaces()), List.of(), members)); // return addDeprecated(e, make.Record(mods, e.getSimpleName(), computeSuper(e.getInterfaces()), members)); case ANNOTATION_TYPE: return addDeprecated(e, make.AnnotationType(mods, e.getSimpleName(), members)); diff --git a/java/java.source/src/org/netbeans/modules/java/ui/FmtCodeGeneration.java b/java/java.source/src/org/netbeans/modules/java/ui/FmtCodeGeneration.java index 3282b7acc28d..d3b24c2278c5 100644 --- a/java/java.source/src/org/netbeans/modules/java/ui/FmtCodeGeneration.java +++ b/java/java.source/src/org/netbeans/modules/java/ui/FmtCodeGeneration.java @@ -529,10 +529,10 @@ protected void doModification(ResultIterator resultIterator) throws Exception { members.add(gu.createConstructor(ct, Collections.emptyList())); members.add(gu.createGetter(field)); ModifiersTree mods = tm.Modifiers(EnumSet.of(Modifier.PRIVATE)); - ClassTree inner = tm.Class(mods, "Inner", Collections.emptyList(), null, Collections.emptyList(), Collections.emptyList()); //NOI18N + ClassTree inner = tm.Class(mods, "Inner", Collections.emptyList(), null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); //NOI18N members.add(inner); mods = tm.Modifiers(EnumSet.of(Modifier.PRIVATE, Modifier.STATIC)); - ClassTree nested = tm.Class(mods, "Nested", Collections.emptyList(), null, Collections.emptyList(), Collections.emptyList()); //NOI18N + ClassTree nested = tm.Class(mods, "Nested", Collections.emptyList(), null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); //NOI18N members.add(nested); IdentifierTree nestedId = tm.Identifier("Nested"); //NOI18N VariableTree staticField = tm.Variable(mods, "instance", nestedId, null); //NOI18N diff --git a/java/java.source/src/org/netbeans/modules/java/ui/FmtNaming.java b/java/java.source/src/org/netbeans/modules/java/ui/FmtNaming.java index 55b5ece92ad9..8ca91242ab4c 100644 --- a/java/java.source/src/org/netbeans/modules/java/ui/FmtNaming.java +++ b/java/java.source/src/org/netbeans/modules/java/ui/FmtNaming.java @@ -28,6 +28,7 @@ import javax.swing.JPanel; import com.sun.source.tree.ClassTree; import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.ModifiersTree; import com.sun.source.tree.Tree; @@ -307,7 +308,7 @@ protected void doModification(ResultIterator resultIterator) throws Exception { members.add(gu.createGetter(booleanField)); members.add(gu.createSetter(ct, booleanField)); ModifiersTree mods = tm.Modifiers(EnumSet.of(Modifier.PRIVATE, Modifier.STATIC)); - ClassTree nested = tm.Class(mods, "Nested", Collections.emptyList(), null, Collections.emptyList(), Collections.emptyList()); //NOI18N + ClassTree nested = tm.Class(mods, "Nested", Collections.emptyList(), null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); //NOI18N members.add(nested); IdentifierTree nestedId = tm.Identifier("Nested"); //NOI18N String instance = CodeStyleUtils.addPrefixSuffix("instance", diff --git a/java/java.source/test/unit/src/org/netbeans/modules/java/classfile/CodeGeneratorTest.java b/java/java.source/test/unit/src/org/netbeans/modules/java/classfile/CodeGeneratorTest.java index 65c563095ee0..f7fc18bbf6bd 100644 --- a/java/java.source/test/unit/src/org/netbeans/modules/java/classfile/CodeGeneratorTest.java +++ b/java/java.source/test/unit/src/org/netbeans/modules/java/classfile/CodeGeneratorTest.java @@ -42,6 +42,8 @@ */ public class CodeGeneratorTest extends ClassIndexTestCase { + private String sourceLevel; + public CodeGeneratorTest(String name) { super(name); } @@ -150,6 +152,23 @@ public void testDecompile1() throws Exception { "}"); } + public void testPermittedSubclasses() throws Exception { + sourceLevel = "17"; + performTest(""" + package test; + public sealed interface Test { + public static final class Impl implements Test {} + } + """, + """ + package test; + public sealed interface Test { + public static final class Impl implements Test { + } + } + """); + } + private void performTest(String test, final String golden) throws Exception { clearWorkDir(); beginTx(); @@ -165,6 +184,11 @@ private void performTest(String test, final String golden) throws Exception { FileObject testFile = FileUtil.createData(src, "test/Test.java"); final FileObject testOutFile = FileUtil.createData(src, "out/Test.java"); TestUtilities.copyStringToFile(testFile, test); + + if (sourceLevel != null) { + SourceUtilsTestUtil.setSourceLevel(testFile, sourceLevel); + } + final ClasspathInfo cpInfo = ClasspathInfoAccessor.getINSTANCE().create(testOutFile, null, true, true, false, true); JavaSource testSource = JavaSource.create(cpInfo, testOutFile); final String[] betterName = new String[1]; diff --git a/java/junit/src/org/netbeans/modules/junit/AbstractTestGenerator.java b/java/junit/src/org/netbeans/modules/junit/AbstractTestGenerator.java index e6bf217602e0..524a35c098ad 100644 --- a/java/junit/src/org/netbeans/modules/junit/AbstractTestGenerator.java +++ b/java/junit/src/org/netbeans/modules/junit/AbstractTestGenerator.java @@ -506,6 +506,7 @@ private ClassTree generateAbstractClassImpl(TypeElement srcClass, Collections.emptyList(), //type params null, //extends implemetnts, //implements + Collections.emptyList(), //permits members); //members default: // should be never happen. We'll generate a class anyway. case CLASS: @@ -516,6 +517,7 @@ private ClassTree generateAbstractClassImpl(TypeElement srcClass, Collections.emptyList(), //type params maker.QualIdent(srcClass), //extends Collections.emptyList(), //implements + Collections.emptyList(), //permits members); //members } } @@ -1007,6 +1009,7 @@ && hasInstanceMethods(srcMethods)) { tstClass.getTypeParameters(), tstClass.getExtendsClause(), (List) tstClass.getImplementsClause(), + (List) tstClass.getPermitsClause(), tstMembers); return newClass; } @@ -1134,6 +1137,7 @@ protected ClassTree generateStubTestMethod(ClassTree tstClass, tstClass.getTypeParameters(), tstClass.getExtendsClause(), (List) tstClass.getImplementsClause(), + (List) tstClass.getPermitsClause(), tstMembers); return newClass; } diff --git a/java/junit/src/org/netbeans/modules/junit/JUnit3TestGenerator.java b/java/junit/src/org/netbeans/modules/junit/JUnit3TestGenerator.java index 1d6e3c4133f2..c057e9a23e1a 100644 --- a/java/junit/src/org/netbeans/modules/junit/JUnit3TestGenerator.java +++ b/java/junit/src/org/netbeans/modules/junit/JUnit3TestGenerator.java @@ -105,6 +105,7 @@ protected ClassTree composeNewTestClass(WorkingCopy workingCopy, Collections.emptyList(),//type params extendsClause, //extends Collections.emptyList(), //implements + Collections.emptyList(), //permits members); //members } @@ -158,6 +159,7 @@ protected ClassTree generateMissingInitMembers(ClassTree tstClass, tstClass.getTypeParameters(), tstClass.getExtendsClause(), (List) tstClass.getImplementsClause(), + (List) tstClass.getPermitsClause(), tstMembers); return newClass; } @@ -358,6 +360,7 @@ protected ClassTree finishSuiteClass(ClassTree tstClass, tstClass.getTypeParameters(), tstClass.getExtendsClause(), (List) tstClass.getImplementsClause(), + (List) tstClass.getPermitsClause(), tstMembers); } diff --git a/java/junit/src/org/netbeans/modules/junit/JUnit4TestGenerator.java b/java/junit/src/org/netbeans/modules/junit/JUnit4TestGenerator.java index a9804ca57789..3ef8b80e34d8 100644 --- a/java/junit/src/org/netbeans/modules/junit/JUnit4TestGenerator.java +++ b/java/junit/src/org/netbeans/modules/junit/JUnit4TestGenerator.java @@ -120,6 +120,7 @@ protected ClassTree composeNewTestClass(WorkingCopy workingCopy, Collections.emptyList(),//type params null, //extends Collections.emptyList(), //implements + Collections.emptyList(), //permits members); //members } @@ -187,6 +188,7 @@ protected ClassTree generateMissingInitMembers(ClassTree tstClass, tstClass.getTypeParameters(), tstClass.getExtendsClause(), (List) tstClass.getImplementsClause(), + (List) tstClass.getPermitsClause(), tstMembers); return newClass; } @@ -435,6 +437,7 @@ protected ClassTree finishSuiteClass(ClassTree tstClass, tstClass.getTypeParameters(), tstClass.getExtendsClause(), (List) tstClass.getImplementsClause(), + (List) tstClass.getPermitsClause(), tstMembers); } diff --git a/java/junit/src/org/netbeans/modules/junit/JUnit5TestGenerator.java b/java/junit/src/org/netbeans/modules/junit/JUnit5TestGenerator.java index f2296eb709a4..01f81505c111 100644 --- a/java/junit/src/org/netbeans/modules/junit/JUnit5TestGenerator.java +++ b/java/junit/src/org/netbeans/modules/junit/JUnit5TestGenerator.java @@ -105,6 +105,7 @@ protected ClassTree composeNewTestClass(WorkingCopy workingCopy, Collections.emptyList(),//type params null, //extends Collections.emptyList(), //implements + Collections.emptyList(), //permits members); //members } @@ -172,6 +173,7 @@ protected ClassTree generateMissingInitMembers(ClassTree tstClass, tstClass.getTypeParameters(), tstClass.getExtendsClause(), (List) tstClass.getImplementsClause(), + (List) tstClass.getPermitsClause(), tstMembers); return newClass; } diff --git a/java/lib.nbjavac/src/org/netbeans/lib/nbjavac/CheckProcessor.java b/java/lib.nbjavac/src/org/netbeans/lib/nbjavac/CheckProcessor.java new file mode 100644 index 000000000000..b2857fd34483 --- /dev/null +++ b/java/lib.nbjavac/src/org/netbeans/lib/nbjavac/CheckProcessor.java @@ -0,0 +1,88 @@ +/* + * 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.lib.nbjavac; + +import com.sun.source.tree.MethodInvocationTree; +import com.sun.source.util.JavacTask; +import com.sun.source.util.TaskEvent; +import com.sun.source.util.TaskListener; +import com.sun.source.util.TreePath; +import com.sun.source.util.TreePathScanner; +import com.sun.source.util.Trees; +import java.util.Set; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.QualifiedNameable; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic; + +@SupportedAnnotationTypes("*") +public class CheckProcessor extends AbstractProcessor implements TaskListener { + + private boolean listenerInstalled; + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (!listenerInstalled) { + JavacTask.instance(processingEnv).addTaskListener(this); + listenerInstalled = true; + } + + return false; + } + + @Override + public void finished(TaskEvent e) { + if (e.getKind() != TaskEvent.Kind.ANALYZE) { + return ; + } + String path = e.getCompilationUnit().getSourceFile().toUri().toString(); + if (path.contains("test") || !path.contains("org/netbeans")) { + //only applies to NetBeans code, and tests are excepted + return ; + } + new TreePathScanner() { + @Override + public Void visitMethodInvocation(MethodInvocationTree node, Void p) { + TreePath selectPath = new TreePath(getCurrentPath(), node.getMethodSelect()); + Trees trees = Trees.instance(processingEnv); + Element el = trees.getElement(selectPath); + + if (el != null && + el.getKind() == ElementKind.METHOD && + ((QualifiedNameable) el.getEnclosingElement()).getQualifiedName().contentEquals("org.netbeans.api.java.source.TreeMaker") && + (el.getSimpleName().contentEquals("Class") || el.getSimpleName().contentEquals("Interface")) && + processingEnv.getElementUtils().isDeprecated(el)) { + trees.printMessage(Diagnostic.Kind.ERROR, "Use of the deprecated TreeMaker.Class/Interface method is not permitted inside the NetBeans sources.", node, selectPath.getCompilationUnit()); + } + + return super.visitMethodInvocation(node, p); + } + }.scan(e.getCompilationUnit(), null); + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } +} diff --git a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/ExtractInterfaceRefactoringPlugin.java b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/ExtractInterfaceRefactoringPlugin.java index dc17fc1b5b62..19b2a9248174 100644 --- a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/ExtractInterfaceRefactoringPlugin.java +++ b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/ExtractInterfaceRefactoringPlugin.java @@ -357,6 +357,7 @@ public void run(WorkingCopy wc) throws Exception { classTree.getTypeParameters(), classTree.getExtendsClause(), impls2Add, + classTree.getPermitsClause(), members2Add); } else if (clazz.getKind() == ElementKind.INTERFACE) { nc = maker.Interface( @@ -364,6 +365,7 @@ public void run(WorkingCopy wc) throws Exception { classTree.getSimpleName(), classTree.getTypeParameters(), impls2Add, + classTree.getPermitsClause(), members2Add); } else if (clazz.getKind() == ElementKind.ENUM) { nc = maker.Enum( @@ -544,6 +546,7 @@ public void createCu(WorkingCopy wc) throws Exception { refactoring.getInterfaceName(), newTypeParams, extendsList, + Collections.emptyList(), Collections.emptyList()); newInterfaceTree = genUtils.insertClassMembers(newInterfaceTree, members); diff --git a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/ExtractSuperclassRefactoringPlugin.java b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/ExtractSuperclassRefactoringPlugin.java index d1d6d4a0be66..faff6ab30989 100644 --- a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/ExtractSuperclassRefactoringPlugin.java +++ b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/ExtractSuperclassRefactoringPlugin.java @@ -337,6 +337,7 @@ public Tree visitClass(ClassTree classTree, Element p) { newTypeParams, superClass, implementsList, + Collections.emptyList(), Collections.emptyList()); newClassTree = GeneratorUtilities.get(workingCopy).insertClassMembers(newClassTree, members); @@ -382,6 +383,7 @@ public Tree visitClass(ClassTree classTree, Element p) { classTree.getTypeParameters(), superClassTree, newImpls, + classTree.getPermitsClause(), newMembers); rewrite(classTree, nc); diff --git a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/IntroduceLocalExtensionTransformer.java b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/IntroduceLocalExtensionTransformer.java index d2492b48305a..83907f3361de 100644 --- a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/IntroduceLocalExtensionTransformer.java +++ b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/IntroduceLocalExtensionTransformer.java @@ -145,6 +145,7 @@ public Tree visitCompilationUnit(CompilationUnitTree node, Element p) { newTypeParams, wrap ? null : make.Type(source.asType()), //superClass, implementsList, + Collections.emptyList(), members); } else { newClassTree = make.Interface( @@ -152,6 +153,7 @@ public Tree visitCompilationUnit(CompilationUnitTree node, Element p) { name, newTypeParams, Collections.singletonList(make.Type(source.asType())), //superClass, + Collections.emptyList(), members); } diff --git a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/MoveClassTransformer.java b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/MoveClassTransformer.java index 8ac02164305d..092abbc72588 100644 --- a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/MoveClassTransformer.java +++ b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/MoveClassTransformer.java @@ -225,6 +225,7 @@ public Object visitIdentifier(IdentifierTree node, Element p) { newClass.getTypeParameters(), newClass.getExtendsClause(), newClass.getImplementsClause(), + newClass.getPermitsClause(), newClass.getMembers()); break; case INTERFACE: @@ -233,6 +234,7 @@ public Object visitIdentifier(IdentifierTree node, Element p) { newClass.getSimpleName(), newClass.getTypeParameters(), newClass.getImplementsClause(), + newClass.getPermitsClause(), newClass.getMembers()); break; case ENUM: diff --git a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/PullUpTransformer.java b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/PullUpTransformer.java index 9d95322e0c9a..be94aadab6fb 100644 --- a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/PullUpTransformer.java +++ b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/PullUpTransformer.java @@ -241,6 +241,7 @@ private ClassTree addMemberToTarget(ClassTree njuClass, Element member, TypeElem oldOne.getTypeParameters(), oldOne.getExtendsClause(), oldOne.getImplementsClause(), + oldOne.getPermitsClause(), oldOne.getMembers()); break; case INTERFACE: @@ -249,6 +250,7 @@ private ClassTree addMemberToTarget(ClassTree njuClass, Element member, TypeElem oldOne.getSimpleName(), oldOne.getTypeParameters(), oldOne.getImplementsClause(), + oldOne.getPermitsClause(), oldOne.getMembers()); break; case ANNOTATION_TYPE: diff --git a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/PushDownTransformer.java b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/PushDownTransformer.java index 85e942f37396..ff890671e3a9 100644 --- a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/PushDownTransformer.java +++ b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/PushDownTransformer.java @@ -315,7 +315,8 @@ private ClassTree rewriteSubClass(Element el, Element source, GeneratorUtilities njuClass = make.Class(RefactoringUtils.makeAbstract(make, njuClass.getModifiers()), njuClass.getSimpleName(), njuClass.getTypeParameters(), njuClass.getExtendsClause(), - njuClass.getImplementsClause(), njuClass.getMembers()); + njuClass.getImplementsClause(), njuClass.getPermitsClause(), + njuClass.getMembers()); } return njuClass; diff --git a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/ReplaceConstructorWithBuilderPlugin.java b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/ReplaceConstructorWithBuilderPlugin.java index e76f5bbb547c..31b479373f85 100644 --- a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/ReplaceConstructorWithBuilderPlugin.java +++ b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/ReplaceConstructorWithBuilderPlugin.java @@ -306,6 +306,7 @@ public void run(WorkingCopy workingCopy) throws Exception { typeParameters, null, Collections.EMPTY_LIST, + Collections.EMPTY_LIST, members); FileObject root = ClassPath.getClassPath(constr.getFileObject(), ClassPath.SOURCE).findOwnerRoot(constr.getFileObject()); CompilationUnitTree builderUnit = make.CompilationUnit(root, refactoring.getBuilderName().replace('.', '/') + ".java", Collections.EMPTY_LIST, Collections.singletonList(builder)); diff --git a/java/refactoring.java/test/unit/src/org/netbeans/modules/refactoring/java/test/RenameSealedTest.java b/java/refactoring.java/test/unit/src/org/netbeans/modules/refactoring/java/test/RenameSealedTest.java new file mode 100644 index 000000000000..c7dbbf64ec52 --- /dev/null +++ b/java/refactoring.java/test/unit/src/org/netbeans/modules/refactoring/java/test/RenameSealedTest.java @@ -0,0 +1,210 @@ +/* + * 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.refactoring.java.test; + +import com.sun.source.tree.ClassTree; +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import org.netbeans.api.java.source.CompilationController; +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.TestUtilities.TestInput; +import org.netbeans.api.java.source.TreePathHandle; +import org.netbeans.modules.refactoring.api.Problem; +import org.netbeans.modules.refactoring.api.RefactoringSession; +import org.netbeans.modules.refactoring.api.RenameRefactoring; +import org.netbeans.modules.refactoring.java.ui.JavaRenameProperties; +import org.netbeans.modules.refactoring.spi.impl.UndoManager; +import org.openide.filesystems.FileObject; +import org.openide.util.lookup.Lookups; + +/** + * + * @author Jan Becicka + */ +public class RenameSealedTest extends RefactoringTestBase { + + public RenameSealedTest(String name) { + super(name, "17"); + } + + public void testRenamePermittedClass0() throws Exception { + String testCode = """ + package test; + public class Outter { + public final class Sub|type implements Test { + } + public sealed interface Test permits Subtype { + } + } + """; + TestInput splitCode = TestUtilities.splitCodeAndPos(testCode); + writeFilesAndWaitForScan(src, + new File("Outter.java", splitCode.code())); + JavaRenameProperties props = new JavaRenameProperties(); + performRename(src.getFileObject("Outter.java"), splitCode.pos(), "NewSubtype", props, true); + verifyContent(src, new File("Outter.java", + """ + package test; + public class Outter { + public final class NewSubtype implements Test { + } + public sealed interface Test permits NewSubtype { + } + } + """)); + + } + + public void testRenamePermittedClass1() throws Exception { + String testCode = """ + package test; + public final class Sub|type implements Test { + } + """; + TestInput splitCode = TestUtilities.splitCodeAndPos(testCode); + writeFilesAndWaitForScan(src, + new File("Test.java", + """ + package test; + public sealed interface Test permits Subtype { + } + """), + new File("Subtype.java", splitCode.code())); + JavaRenameProperties props = new JavaRenameProperties(); + performRename(src.getFileObject("Subtype.java"), splitCode.pos(), "NewSubtype", props, true); + verifyContent(src, new File("Test.java", + """ + package test; + public sealed interface Test permits NewSubtype { + } + """), + new File("Subtype.java", + """ + package test; + public final class NewSubtype implements Test { + } + """)); + + } + + public void testRenamePermittedClass2() throws Exception { + String testCode = """ + package test; + public sealed interface Test permits Sub|type { + } + """; + TestInput splitCode = TestUtilities.splitCodeAndPos(testCode); + writeFilesAndWaitForScan(src, + new File("Test.java", splitCode.code()), + new File("Subtype.java", + """ + package test; + public class Subtype implements Test { + } + """)); + JavaRenameProperties props = new JavaRenameProperties(); + performRename(src.getFileObject("Test.java"), splitCode.pos(), "NewSubtype", props, true); + verifyContent(src, new File("Test.java", + """ + package test; + public sealed interface Test permits NewSubtype { + } + """), + new File("Subtype.java", + """ + package test; + public class NewSubtype implements Test { + } + """)); + + } + + public void testRenameInterfaceWithPermittedClass() throws Exception { + String testCode = """ + package test; + public sealed interface Te|st permits Subtype { + } + """; + TestInput splitCode = TestUtilities.splitCodeAndPos(testCode); + writeFilesAndWaitForScan(src, + new File("Test.java", splitCode.code()), + new File("Subtype.java", + """ + package test; + public class Subtype implements Test { + } + """)); + JavaRenameProperties props = new JavaRenameProperties(); + performRename(src.getFileObject("Test.java"), splitCode.pos(), "Test2", props, true); + verifyContent(src, new File("Test.java", + """ + package test; + public sealed interface Test2 permits Subtype { + } + """), + new File("Subtype.java", + """ + package test; + public class Subtype implements Test2 { + } + """)); + + } + + private void performRename(FileObject source, final int absPos, final String newname, final JavaRenameProperties props, final boolean searchInComments, Problem... expectedProblems) throws Exception { + final RenameRefactoring[] r = new RenameRefactoring[1]; + JavaSource.forFileObject(source).runUserActionTask(new Task() { + + @Override + public void run(CompilationController javac) throws Exception { + javac.toPhase(JavaSource.Phase.RESOLVED); + CompilationUnitTree cut = javac.getCompilationUnit(); + + TreePath tp = javac.getTreeUtilities().pathFor(absPos); + + r[0] = new RenameRefactoring(Lookups.singleton(TreePathHandle.create(tp, javac))); + r[0].setNewName(newname); + r[0].setSearchInComments(searchInComments); + if(props != null) { + r[0].getContext().add(props); + } + } + }, true); + + RefactoringSession rs = RefactoringSession.create("Rename"); + List problems = new LinkedList<>(); + + addAllProblems(problems, r[0].preCheck()); + if (!problemIsFatal(problems)) { + addAllProblems(problems, r[0].prepare(rs)); + } + if (!problemIsFatal(problems)) { + addAllProblems(problems, rs.doRefactoring(true)); + } + + assertProblems(Arrays.asList(expectedProblems), problems); + } + +} diff --git a/java/spi.java.hints/src/org/netbeans/modules/java/hints/spiimpl/Utilities.java b/java/spi.java.hints/src/org/netbeans/modules/java/hints/spiimpl/Utilities.java index 7081b179a542..ca3a8a904c2b 100644 --- a/java/spi.java.hints/src/org/netbeans/modules/java/hints/spiimpl/Utilities.java +++ b/java/spi.java.hints/src/org/netbeans/modules/java/hints/spiimpl/Utilities.java @@ -501,7 +501,7 @@ private static Tree parseAndAttribute(CompilationInfo info, JavacTaskImpl jti, S newMembers.add(make.ExpressionStatement(make.Identifier("$$1$"))); newMembers.addAll(members.subList(syntheticOffset, members.size())); - patternTree = make.Class(mt, "$", List.of(), null, List.of(), newMembers); + patternTree = make.Class(mt, "$", List.of(), null, List.of(), List.of(), newMembers); } else { patternTree = members.get(0 + syntheticOffset); } diff --git a/java/spi.java.hints/src/org/netbeans/spi/java/hints/JavaFixUtilities.java b/java/spi.java.hints/src/org/netbeans/spi/java/hints/JavaFixUtilities.java index 5a7f1ff58c41..7e8ef5f5ad89 100644 --- a/java/spi.java.hints/src/org/netbeans/spi/java/hints/JavaFixUtilities.java +++ b/java/spi.java.hints/src/org/netbeans/spi/java/hints/JavaFixUtilities.java @@ -584,7 +584,7 @@ protected void performRewrite(TransformationContext ctx) { } } - rewriteFromTo.put(original = parent.getLeaf(), wc.getTreeMaker().Class(ct.getModifiers(), ct.getSimpleName(), ct.getTypeParameters(), ct.getExtendsClause(), ct.getImplementsClause(), newMembers)); + rewriteFromTo.put(original = parent.getLeaf(), wc.getTreeMaker().Class(ct.getModifiers(), ct.getSimpleName(), ct.getTypeParameters(), ct.getExtendsClause(), ct.getImplementsClause(), ct.getPermitsClause(), newMembers)); } else if (tp.getLeaf().getKind() == Kind.BLOCK && parametersMulti.containsKey("$$1$") && parsed.getKind() != Kind.BLOCK && StatementTree.class.isAssignableFrom(parsed.getKind().asInterface())) { List newStatements = new LinkedList<>(); @@ -956,9 +956,10 @@ public Number visitClass(ClassTree node, Void p) { List typeParams = resolveMultiParameters(node.getTypeParameters()); List implementsClauses = resolveMultiParameters(node.getImplementsClause()); + List permitsClauses = resolveMultiParameters(node.getPermitsClause()); List members = resolveMultiParameters(Utilities.filterHidden(getCurrentPath(), node.getMembers())); Tree extend = resolveOptionalValue(node.getExtendsClause()); - ClassTree nue = make.Class(node.getModifiers(), newName, typeParams, extend, implementsClauses, members); + ClassTree nue = make.Class(node.getModifiers(), newName, typeParams, extend, implementsClauses, permitsClauses, members); rewrite(node, nue); @@ -1697,7 +1698,7 @@ private void doRemoveFromParent(WorkingCopy wc, TreePath what) { if (classTree.getTypeParameters().contains(leaf)) { nueClassTree = make.removeClassTypeParameter(classTree, (TypeParameterTree) leaf); } else if (classTree.getExtendsClause() == leaf) { - nueClassTree = make.Class(classTree.getModifiers(), classTree.getSimpleName(), classTree.getTypeParameters(), null, classTree.getImplementsClause(), classTree.getMembers()); + nueClassTree = make.setExtends(classTree, null); } else if (classTree.getImplementsClause().contains(leaf)) { nueClassTree = make.removeClassImplementsClause(classTree, leaf); } else if (classTree.getMembers().contains(leaf)) { diff --git a/java/testng/src/org/netbeans/modules/testng/AbstractTestGenerator.java b/java/testng/src/org/netbeans/modules/testng/AbstractTestGenerator.java index 32346c903b6a..752ff2714236 100644 --- a/java/testng/src/org/netbeans/modules/testng/AbstractTestGenerator.java +++ b/java/testng/src/org/netbeans/modules/testng/AbstractTestGenerator.java @@ -500,6 +500,7 @@ private ClassTree generateAbstractClassImpl(TypeElement srcClass, Collections.emptyList(), //type params null, //extends implemetnts, //implements + Collections.emptyList(), //permits members); //members default: // should be never happen. We'll generate a class anyway. case CLASS: @@ -510,6 +511,7 @@ private ClassTree generateAbstractClassImpl(TypeElement srcClass, Collections.emptyList(), //type params maker.QualIdent(srcClass), //extends Collections.emptyList(), //implements + Collections.emptyList(), //permits members); //members } } @@ -1001,6 +1003,7 @@ && hasInstanceMethods(srcMethods)) { tstClass.getTypeParameters(), tstClass.getExtendsClause(), (List) tstClass.getImplementsClause(), + (List) tstClass.getPermitsClause(), tstMembers); return newClass; } @@ -1134,6 +1137,7 @@ protected ClassTree generateStubTestMethod(ClassTree tstClass, tstClass.getTypeParameters(), tstClass.getExtendsClause(), (List) tstClass.getImplementsClause(), + (List) tstClass.getPermitsClause(), tstMembers); return newClass; } diff --git a/java/testng/src/org/netbeans/modules/testng/TestGenerator.java b/java/testng/src/org/netbeans/modules/testng/TestGenerator.java index b15184dbd1c6..e917efa06b86 100644 --- a/java/testng/src/org/netbeans/modules/testng/TestGenerator.java +++ b/java/testng/src/org/netbeans/modules/testng/TestGenerator.java @@ -119,6 +119,7 @@ protected ClassTree composeNewTestClass(WorkingCopy workingCopy, Collections.emptyList(),//type params null, //extends Collections.emptyList(), //implements + Collections.emptyList(), //permits members); //members } @@ -186,6 +187,7 @@ protected ClassTree generateMissingInitMembers(ClassTree tstClass, tstClass.getTypeParameters(), tstClass.getExtendsClause(), (List) tstClass.getImplementsClause(), + (List) tstClass.getPermitsClause(), tstMembers); return newClass; }