Bug 574526 - [17][quick fix] Provide quick fix to add a type to
permitted types of a sealed type

Change-Id: I574ba1be0ad691f5fd3c0a032fb0cff486c05099
Signed-off-by: Kalyan Prasad Tatavarthi <kalyan_prasad@in.ibm.com>
Reviewed-on: https://git.eclipse.org/r/c/jdt/eclipse.jdt.ui/+/182694
Tested-by: JDT Bot <jdt-bot@eclipse.org>
diff --git a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.java b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.java
index 6beec87..b8a8f6b 100644
--- a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.java
+++ b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.java
@@ -462,7 +462,8 @@
 	public static String LocalCorrectionsSubProcessor_replace_with_unqualified_enum_constant;
 	public static String LocalCorrectionsSubProcessor_throw_allocated_description;
 	public static String LocalCorrectionsSubProcessor_add_provider_method_description;
-	public static String localCorrectionsSubProcessor_declareSealedAsDirectSuperInterface_description;
+	public static String LocalCorrectionsSubProcessor_declareSealedAsDirectSuperInterface_description;
+	public static String LocalCorrectionsSubProcessor_declareSubClassAsPermitsSealedClass_description;
 	public static String SuppressWarningsSubProcessor_fix_suppress_token_label;
 	public static String SuppressWarningsSubProcessor_remove_annotation_label;
 	public static String VarargsWarningsSubProcessor_add_safevarargs_label;
diff --git a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.properties b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.properties
index cf66f64..c16570e 100644
--- a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.properties
+++ b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.properties
@@ -136,7 +136,8 @@
 LocalCorrectionsSubProcessor_throw_allocated_description=Throw the allocated object
 LocalCorrectionsSubProcessor_replace_with_unqualified_enum_constant=Replace with unqualified enum constant ''{0}''
 LocalCorrectionsSubProcessor_add_provider_method_description=Create ''{0}'' provider method
-localCorrectionsSubProcessor_declareSealedAsDirectSuperInterface_description=Declare ''{0}'' as direct super interface of ''{1}''
+LocalCorrectionsSubProcessor_declareSealedAsDirectSuperInterface_description=Declare ''{0}'' as direct super interface of ''{1}''
+LocalCorrectionsSubProcessor_declareSubClassAsPermitsSealedClass_description=Declare ''{0}'' as permitted type of ''{1}''
 TypeMismatchSubProcessor_addcast_description=Add cast to ''{0}''
 TypeMismatchSubProcessor_changecast_description=Change cast to ''{0}''
 TypeMismatchSubProcessor_changereturntype_description=Change method return type to ''{0}''
diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/QuickFixTestPreview.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/QuickFixTestPreview.java
index 7216012..269d97c 100644
--- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/QuickFixTestPreview.java
+++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/QuickFixTestPreview.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2019, 2020 IBM Corporation and others.
+ * Copyright (c) 2019, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -160,5 +160,156 @@
 
 	}
 
+	@Test
+	public void testAddSealedAsDirectSuperTypeProposal1() throws Exception {
+		fJProject1= JavaProjectHelper.createJavaProject("TestProject1", "bin");
+		fJProject1.setRawClasspath(projectsetup.getDefaultClasspath(), null);
+		JavaProjectHelper.set16CompilerOptions(fJProject1, true);
 
+		Map<String, String> options= fJProject1.getOptions(false);
+		options.put(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES, JavaCore.IGNORE);
+		fJProject1.setOptions(options);
+
+		fSourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src");
+		IPackageFragment pack1= fSourceFolder.createPackageFragment("test", false, null);
+
+		String test= "" +
+				"package test;\n" +
+				"\n" +
+				"public sealed class Shape permits Square {\n" +
+				"}\n";
+		pack1.createCompilationUnit("Shape.java", test, false, null);
+
+		test= "" +
+				"package test;\n" +
+				"\n" +
+				"public non-sealed class Square extends Shape {\n" +
+				"}\n";
+		pack1.createCompilationUnit("Square.java", test, false, null);
+
+		test= "" +
+				"package test;\n" +
+				"\n" +
+				"public non-sealed class Circle extends Shape {\n" +
+				"}\n";
+		ICompilationUnit cu2= pack1.createCompilationUnit("Circle.java", test, false, null);
+
+		CompilationUnit astRoot= getASTRoot(cu2);
+		ArrayList<IJavaCompletionProposal> proposals= collectCorrections(cu2, astRoot, 1);
+		assertNumberOfProposals(proposals, 1);
+		assertCorrectLabels(proposals);
+
+		CUCorrectionProposal proposal= (CUCorrectionProposal) proposals.get(0);
+		String preview= getPreviewContent(proposal);
+
+		String expected= "" +
+				"package test;\n" +
+				"\n" +
+				"public sealed class Shape permits Square, Circle {\n" +
+				"}\n";
+
+		assertEqualString(preview, expected);
+	}
+
+	@Test
+	public void testAddSealedAsDirectSuperTypeProposal2() throws Exception {
+		fJProject1= JavaProjectHelper.createJavaProject("TestProject1", "bin");
+		fJProject1.setRawClasspath(projectsetup.getDefaultClasspath(), null);
+		JavaProjectHelper.set16CompilerOptions(fJProject1, true);
+
+		Map<String, String> options= fJProject1.getOptions(false);
+		options.put(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES, JavaCore.IGNORE);
+		fJProject1.setOptions(options);
+
+		fSourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src");
+		IPackageFragment pack1= fSourceFolder.createPackageFragment("test", false, null);
+
+		String test= "" +
+				"package test;\n" +
+				"\n" +
+				"public sealed interface Shape permits Square {\n" +
+				"}\n";
+		pack1.createCompilationUnit("Shape.java", test, false, null);
+
+		test= "" +
+				"package test;\n" +
+				"\n" +
+				"public non-sealed class Square implements Shape {\n" +
+				"}\n";
+		pack1.createCompilationUnit("Square.java", test, false, null);
+
+		test= "" +
+				"package test;\n" +
+				"\n" +
+				"public non-sealed class Circle implements Shape {\n" +
+				"}\n";
+		ICompilationUnit cu2= pack1.createCompilationUnit("Circle.java", test, false, null);
+
+		CompilationUnit astRoot= getASTRoot(cu2);
+		ArrayList<IJavaCompletionProposal> proposals= collectCorrections(cu2, astRoot, 1);
+		assertNumberOfProposals(proposals, 1);
+		assertCorrectLabels(proposals);
+
+		CUCorrectionProposal proposal= (CUCorrectionProposal) proposals.get(0);
+		String preview= getPreviewContent(proposal);
+
+		String expected= "" +
+				"package test;\n" +
+				"\n" +
+				"public sealed interface Shape permits Square, Circle {\n" +
+				"}\n";
+
+		assertEqualString(preview, expected);
+	}
+
+	@Test
+	public void testAddSealedAsDirectSuperTypeProposal3() throws Exception {
+		fJProject1= JavaProjectHelper.createJavaProject("TestProject1", "bin");
+		fJProject1.setRawClasspath(projectsetup.getDefaultClasspath(), null);
+		JavaProjectHelper.set16CompilerOptions(fJProject1, true);
+
+		Map<String, String> options= fJProject1.getOptions(false);
+		options.put(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES, JavaCore.IGNORE);
+		fJProject1.setOptions(options);
+
+		fSourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src");
+		IPackageFragment pack1= fSourceFolder.createPackageFragment("test", false, null);
+
+		String test= "" +
+				"package test;\n" +
+				"\n" +
+				"public sealed interface Shape permits Square {\n" +
+				"}\n";
+		pack1.createCompilationUnit("Shape.java", test, false, null);
+
+		test= "" +
+				"package test;\n" +
+				"\n" +
+				"public non-sealed class Square implements Shape {\n" +
+				"}\n";
+		pack1.createCompilationUnit("Square.java", test, false, null);
+
+		test= "" +
+				"package test;\n" +
+				"\n" +
+				"public non-sealed interface Circle extends Shape {\n" +
+				"}\n";
+		ICompilationUnit cu2= pack1.createCompilationUnit("Circle.java", test, false, null);
+
+		CompilationUnit astRoot= getASTRoot(cu2);
+		ArrayList<IJavaCompletionProposal> proposals= collectCorrections(cu2, astRoot, 1);
+		assertNumberOfProposals(proposals, 1);
+		assertCorrectLabels(proposals);
+
+		CUCorrectionProposal proposal= (CUCorrectionProposal) proposals.get(0);
+		String preview= getPreviewContent(proposal);
+
+		String expected= "" +
+				"package test;\n" +
+				"\n" +
+				"public sealed interface Shape permits Square, Circle {\n" +
+				"}\n";
+
+		assertEqualString(preview, expected);
+	}
 }
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/LocalCorrectionsSubProcessor.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/LocalCorrectionsSubProcessor.java
index c02cea7..5931b13 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/LocalCorrectionsSubProcessor.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/LocalCorrectionsSubProcessor.java
@@ -1305,6 +1305,59 @@
 		}
 	}
 
+	public static void addTypeAsPermittedSubTypeProposal(IInvocationContext context, IProblemLocation problem, Collection<ICommandAccess> proposals) throws JavaModelException {
+		ASTNode selectedNode= problem.getCoveringNode(context.getASTRoot());
+		if (selectedNode == null) {
+			return;
+		}
+		if (!ASTHelper.isSealedTypeSupportedInAST(selectedNode.getAST())) {
+			return;
+		}
+
+		while (selectedNode.getParent() instanceof Type) {
+			selectedNode= selectedNode.getParent();
+		}
+		if (selectedNode.getLocationInParent() != TypeDeclaration.SUPERCLASS_TYPE_PROPERTY
+				&& selectedNode.getLocationInParent() != TypeDeclaration.SUPER_INTERFACE_TYPES_PROPERTY) {
+			return;
+		}
+		TypeDeclaration subType= (TypeDeclaration) selectedNode.getParent();
+
+		IJavaElement sealedTypeElement= null;
+		if (selectedNode instanceof SimpleType) {
+			ITypeBinding typeBinding= ((SimpleType) selectedNode).resolveBinding();
+			if (typeBinding != null) {
+				sealedTypeElement= typeBinding.getJavaElement();
+			}
+		}
+		if (!(sealedTypeElement instanceof IType)) {
+			return;
+		}
+		IType sealedType= (IType) sealedTypeElement;
+		if (sealedType.isBinary() || !sealedType.isSealed()) {
+			return;
+		}
+		ICompilationUnit compilationUnit= sealedType.getCompilationUnit();
+		CompilationUnitRewrite cuRewrite= new CompilationUnitRewrite(compilationUnit);
+		TypeDeclaration declaration= ASTNodeSearchUtil.getTypeDeclarationNode(sealedType, cuRewrite.getRoot());
+		if (declaration == null) {
+			return;
+		}
+
+		AST ast= declaration.getAST();
+		String subTypeName= subType.getName().getIdentifier();
+		Type type= ast.newSimpleType(ast.newSimpleName(subTypeName));
+
+		ASTRewrite astRewrite= cuRewrite.getASTRewrite();
+		astRewrite.getListRewrite(declaration, TypeDeclaration.PERMITS_TYPES_PROPERTY).insertLast(type, null);
+
+		String sealedTypeName= problem.getProblemArguments()[1];
+		String label= Messages.format(CorrectionMessages.LocalCorrectionsSubProcessor_declareSubClassAsPermitsSealedClass_description, new String[] { subTypeName, sealedTypeName });
+		Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_ADD);
+		ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, compilationUnit, astRewrite, IProposalRelevance.DECLARE_SEALED_AS_DIRECT_SUPER_TYPE, image);
+		proposals.add(proposal);
+	}
+
 	public static void addSealedAsDirectSuperTypeProposal(IInvocationContext context, IProblemLocation problem, Collection<ICommandAccess> proposals) throws JavaModelException {
 		ASTNode selectedNode= problem.getCoveringNode(context.getASTRoot());
 		if (selectedNode == null) {
@@ -1334,10 +1387,6 @@
 		}
 
 		ICompilationUnit compilationUnit= ((IType) permittedTypeElement).getCompilationUnit();
-		if (compilationUnit == null) {
-			return;
-		}
-
 		CompilationUnitRewrite cuRewrite= new CompilationUnitRewrite(compilationUnit);
 		TypeDeclaration declaration= ASTNodeSearchUtil.getTypeDeclarationNode((IType) permittedTypeElement, cuRewrite.getRoot());
 		if (declaration == null) {
@@ -1352,7 +1401,7 @@
 		astRewrite.getListRewrite(declaration, TypeDeclaration.SUPER_INTERFACE_TYPES_PROPERTY).insertLast(type, null);
 
 		String permittedTypeName= problem.getProblemArguments()[0];
-		String label= Messages.format(CorrectionMessages.localCorrectionsSubProcessor_declareSealedAsDirectSuperInterface_description, new String[] { sealedTypeName, permittedTypeName });
+		String label= Messages.format(CorrectionMessages.LocalCorrectionsSubProcessor_declareSealedAsDirectSuperInterface_description, new String[] { sealedTypeName, permittedTypeName });
 		Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_ADD);
 		ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, compilationUnit, astRewrite, IProposalRelevance.DECLARE_SEALED_AS_DIRECT_SUPER_TYPE, image);
 		proposals.add(proposal);
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickFixProcessor.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickFixProcessor.java
index 75cb271..6d34e41 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickFixProcessor.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickFixProcessor.java
@@ -131,6 +131,8 @@
 			case IProblem.SealedMissingClassModifier:
 			case IProblem.SealedMissingInterfaceModifier:
 			case IProblem.SealedNotDirectSuperInterface:
+			case IProblem.SealedSuperClassDoesNotPermit:
+			case IProblem.SealedSuperInterfaceDoesNotPermit:
 			case IProblem.Task:
 			case IProblem.UnusedMethodDeclaredThrownException:
 			case IProblem.UnusedConstructorDeclaredThrownException:
@@ -473,6 +475,10 @@
 			case IProblem.SealedNotDirectSuperInterface:
 				LocalCorrectionsSubProcessor.addSealedAsDirectSuperTypeProposal(context, problem, proposals);
 				break;
+			case IProblem.SealedSuperClassDoesNotPermit:
+			case IProblem.SealedSuperInterfaceDoesNotPermit:
+				LocalCorrectionsSubProcessor.addTypeAsPermittedSubTypeProposal(context, problem, proposals);
+				break;
 			case IProblem.StaticMethodRequested:
 			case IProblem.NonStaticFieldFromStaticInvocation:
 				LocalCorrectionsSubProcessor.addObjectReferenceProposal(context, problem, proposals);