blob: d340750baed8ae12d7b14f84d2b6ac3fb73f1ec6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.ui.tests.core.source;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Hashtable;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.eclipse.jdt.testplugin.JavaProjectHelper;
import org.eclipse.jdt.testplugin.TestOptions;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IImportDeclaration;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.NodeFinder;
import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
import org.eclipse.jdt.internal.core.manipulation.CodeTemplateContextType;
import org.eclipse.jdt.internal.core.manipulation.StubUtility;
import org.eclipse.jdt.internal.core.manipulation.util.Strings;
import org.eclipse.jdt.internal.corext.codemanipulation.AddUnimplementedMethodsOperation;
import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility2Core;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.dom.IASTSharedValues;
import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jdt.ui.tests.core.rules.ProjectTestSetup;
public class AddUnimplementedMethodsTest {
@Rule
public ProjectTestSetup pts= new ProjectTestSetup();
private IJavaProject fJavaProject;
private IPackageFragment fPackage;
private IType fClassA, fInterfaceB, fClassC, fClassD, fInterfaceE;
@Before
public void setUp() throws Exception {
fJavaProject= JavaProjectHelper.createJavaProject("DummyProject", "bin");
Hashtable<String, String> options= TestOptions.getDefaultOptions();
options.put(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, JavaCore.SPACE);
options.put(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, "4");
options.put(DefaultCodeFormatterConstants.FORMATTER_LINE_SPLIT, "999");
fJavaProject.setOptions(options);
assertNotNull(JavaProjectHelper.addRTJar(fJavaProject));
StubUtility.setCodeTemplate(CodeTemplateContextType.METHODSTUB_ID, "${body_statement}\n// TODO", null);
IPackageFragmentRoot root= JavaProjectHelper.addSourceContainer(fJavaProject, "src");
fPackage= root.createPackageFragment("ibm.util", true, null);
ICompilationUnit cu= fPackage.getCompilationUnit("A.java");
fClassA= cu.createType("public abstract class A {\n}\n", null, true, null);
fClassA.createMethod("public abstract void a();\n", null, true, null);
fClassA.createMethod("public abstract void b(java.util.Vector<java.util.Date> v);\n", null, true, null);
cu= fPackage.getCompilationUnit("B.java");
fInterfaceB= cu.createType("public interface B {\n}\n", null, true, null);
fInterfaceB.createMethod("void c(java.util.Hashtable h);\n", null, true, null);
cu= fPackage.getCompilationUnit("C.java");
fClassC= cu.createType("public abstract class C {\n}\n", null, true, null);
fClassC.createMethod("public void c(java.util.Hashtable h) {\n}\n", null, true, null);
fClassC.createMethod("public abstract java.util.Enumeration d(java.util.Hashtable h) {\n}\n", null, true, null);
cu= fPackage.getCompilationUnit("D.java");
fClassD= cu.createType("public abstract class D extends C {\n}\n", null, true, null);
fClassD.createMethod("public abstract void c(java.util.Hashtable h);\n", null, true, null);
cu= fPackage.getCompilationUnit("E.java");
fInterfaceE= cu.createType("public interface E {\n}\n", null, true, null);
fInterfaceE.createMethod("void c(java.util.Hashtable h);\n", null, true, null);
fInterfaceE.createMethod("void e() throws java.util.NoSuchElementException;\n", null, true, null);
IEclipsePreferences node= new ProjectScope(fJavaProject.getProject()).getNode(JavaUI.ID_PLUGIN);
node.putBoolean(PreferenceConstants.CODEGEN_USE_OVERRIDE_ANNOTATION, false);
node.putBoolean(PreferenceConstants.CODEGEN_ADD_COMMENTS, false);
node.flush();
}
@After
public void tearDown () throws Exception {
JavaProjectHelper.delete(fJavaProject);
fJavaProject= null;
fPackage= null;
fClassA= null;
fInterfaceB= null;
fClassC= null;
fClassD= null;
fInterfaceE= null;
}
/*
* basic test: extend an abstract class and an interface
*/
@Test
public void test1() throws Exception {
ICompilationUnit cu= fPackage.getCompilationUnit("Test1.java");
IType testClass= cu.createType("public class Test1 extends A implements B {\n}\n", null, true, null);
testHelper(testClass);
IMethod[] methods= testClass.getMethods();
checkMethods(new String[] { "a", "b", "c", "equals", "clone", "toString", "finalize", "hashCode" }, methods);
IImportDeclaration[] imports= cu.getImports();
checkImports(new String[] { "java.util.Date", "java.util.Hashtable", "java.util.Vector" }, imports);
}
/*
* method c() of interface B is already implemented by class C
*/
@Test
public void test2() throws Exception {
ICompilationUnit cu= fPackage.getCompilationUnit("Test2.java");
IType testClass= cu.createType("public class Test2 extends C implements B {\n}\n", null, true, null);
testHelper(testClass);
IMethod[] methods= testClass.getMethods();
checkMethods(new String[] { "c", "d", "equals", "clone", "toString", "finalize", "hashCode" }, methods);
IImportDeclaration[] imports= cu.getImports();
checkImports(new String[] { "java.util.Enumeration", "java.util.Hashtable" }, imports);
}
/*
* method c() is implemented in C but made abstract again in class D
*/
@Test
public void test3() throws Exception {
ICompilationUnit cu= fPackage.getCompilationUnit("Test3.java");
IType testClass= cu.createType("public class Test3 extends D {\n}\n", null, true, null);
testHelper(testClass);
IMethod[] methods= testClass.getMethods();
checkMethods(new String[] { "c", "d", "equals", "clone", "toString", "finalize", "hashCode" }, methods);
IImportDeclaration[] imports= cu.getImports();
checkImports(new String[] { "java.util.Hashtable", "java.util.Enumeration" }, imports);
}
/*
* method c() defined in both interfaces B and E
*/
@Test
public void test4() throws Exception {
ICompilationUnit cu= fPackage.getCompilationUnit("Test4.java");
IType testClass= cu.createType("public class Test4 implements B, E {\n}\n", null, true, null);
testHelper(testClass);
IMethod[] methods= testClass.getMethods();
checkMethods(new String[] { "c", "e", "equals", "clone", "toString", "finalize", "hashCode" }, methods);
IImportDeclaration[] imports= cu.getImports();
checkImports(new String[] { "java.util.Hashtable", "java.util.NoSuchElementException" }, imports);
}
@Test
public void bug119171() throws Exception {
StringBuffer buf= new StringBuffer();
buf.append("package ibm.util;\n");
buf.append("import java.util.Properties;\n");
buf.append("public interface F {\n");
buf.append(" public void b(Properties p);\n");
buf.append("}\n");
fPackage.createCompilationUnit("F.java", buf.toString(), false, null);
buf= new StringBuffer();
buf.append("package ibm.util;\n");
buf.append("public class Properties {\n");
buf.append(" public int get() {return 0;}\n");
buf.append("}\n");
fPackage.createCompilationUnit("Properties.java", buf.toString(), false, null);
buf= new StringBuffer();
buf.append("public class Test5 implements F {\n");
buf.append(" public void foo() {\n");
buf.append(" Properties p= new Properties();\n");
buf.append(" p.get();\n");
buf.append(" }\n");
buf.append("}\n");
ICompilationUnit cu= fPackage.getCompilationUnit("Test5.java");
IType testClass= cu.createType(buf.toString(), null, true, null);
testHelper(testClass);
IMethod[] methods= testClass.getMethods();
checkMethodsInOrder(new String[] { "foo", "b", "clone", "equals", "finalize", "hashCode", "toString"}, methods);
IImportDeclaration[] imports= cu.getImports();
checkImports(new String[0], imports);
}
@Test
public void bug297183() throws Exception {
StringBuffer buf= new StringBuffer();
buf.append("package ibm.util;\n");
buf.append("interface Shape {\r\n");
buf.append(" int getX();\r\n");
buf.append(" int getY();\r\n");
buf.append(" int getEdges();\r\n");
buf.append(" int getArea();\r\n");
buf.append("}\r\n");
fPackage.createCompilationUnit("Shape.java", buf.toString(), false, null);
buf= new StringBuffer();
buf.append("package ibm.util;\n");
buf.append("interface Circle extends Shape {\r\n");
buf.append(" int getR();\r\n");
buf.append("}\r\n");
buf.append("\r\n");
fPackage.createCompilationUnit("Circle.java", buf.toString(), false, null);
buf= new StringBuffer();
buf.append("package ibm.util;\n");
buf.append("public class DefaultCircle implements Circle {\n");
buf.append("}\n");
ICompilationUnit cu= fPackage.getCompilationUnit("DefaultCircle.java");
IType testClass= cu.createType(buf.toString(), null, true, null);
testHelper(testClass, -1, false);
IMethod[] methods= testClass.getMethods();
checkMethodsInOrder(new String[] { "getX", "getY", "getEdges", "getArea", "getR"}, methods);
IImportDeclaration[] imports= cu.getImports();
checkImports(new String[0], imports);
}
@Test
public void insertAt() throws Exception {
fJavaProject= JavaProjectHelper.createJavaProject("DummyProject", "bin");
assertNotNull(JavaProjectHelper.addRTJar(fJavaProject));
IPackageFragmentRoot root= JavaProjectHelper.addSourceContainer(fJavaProject, "src");
fPackage= root.createPackageFragment("p", true, null);
StringBuffer buf= new StringBuffer();
buf.append("package p;\n");
buf.append("\n");
buf.append("public abstract class B {\n");
buf.append(" public abstract void foo() {\n");
buf.append(" }\n");
buf.append("}");
fPackage.createCompilationUnit("B.java", buf.toString(), true, null);
buf= new StringBuffer();
buf.append("package p;\n");
buf.append("\n");
buf.append("public class A extends B {\n");
buf.append(" int x;\n");
buf.append("\n");
buf.append(" A() {\n");
buf.append(" }\n");
buf.append("\n");
buf.append(" void bar() {\n");
buf.append(" }\n");
buf.append("\n");
buf.append(" {\n"); // initializer
buf.append(" }\n");
buf.append("\n");
buf.append(" static {\n"); // static initializer
buf.append(" }\n");
buf.append("\n");
buf.append(" class Inner {\n"); // inner class
buf.append(" }\n");
buf.append("}");
String originalContent= buf.toString();
final int NUM_MEMBERS= 6;
buf= new StringBuffer();
buf.append("public void foo() {\n");
buf.append(" // TODO\n");
buf.append(" }");
String expectedConstructor= buf.toString();
// try to insert the new constructor after every member and at the end
for (int i= 0; i < NUM_MEMBERS + 1; i++) {
ICompilationUnit unit= null;
try {
unit= fPackage.createCompilationUnit("A.java", originalContent, true, null);
IType type= unit.findPrimaryType();
IJavaElement[] children= type.getChildren();
assertEquals(NUM_MEMBERS, children.length);
int insertIndex= i < NUM_MEMBERS ? ((IMember) children[i]).getSourceRange().getOffset() : -1;
testHelper(type, insertIndex, false);
IJavaElement[] newChildren= type.getChildren();
assertEquals(NUM_MEMBERS + 1, newChildren.length);
String source= ((IMember) newChildren[i]).getSource(); // new element expected at index i
assertEquals(expectedConstructor, source);
} finally {
if (unit != null) {
JavaProjectHelper.delete(unit);
}
}
}
}
@Test
public void bug480682() throws Exception {
StringBuilder buf= new StringBuilder();
buf.append("public class Test480682 extends Base {\n");
buf.append("}\n");
buf.append("abstract class Base implements I {\n");
buf.append(" @Override\n");
buf.append(" public final void method1() {}\n");
buf.append("}\n");
buf.append("interface I {\n");
buf.append(" void method1();\n");
buf.append(" void method2();\n");
buf.append("}\n");
ICompilationUnit cu= fPackage.createCompilationUnit("Test480682.java", buf.toString(), true, null);
IType testClass= cu.createType(buf.toString(), null, true, null);
testHelper(testClass, -1, false);
IMethod[] methods= testClass.getMethods();
checkMethods(new String[] { "method2" }, methods);
}
/**
* @throws Exception
* @deprecated tests deprecated API
*/
@Deprecated
@Test
public void jLS3() throws Exception {
doTestOldAstLevel(AST.JLS3);
}
/**
* @throws Exception
* @deprecated tests deprecated API
*/
@Deprecated
@Test
public void jLS4() throws Exception {
doTestOldAstLevel(AST.JLS4);
}
@Test
public void jLS8() throws Exception {
doTestOldAstLevel(AST.JLS8);
}
/**
* @param astLevel AST.JLS*
* @throws Exception
* @deprecated tests deprecated API
*/
@Deprecated
public void doTestOldAstLevel(int astLevel) throws Exception {
ICompilationUnit cu= fPackage.getCompilationUnit("Test1.java");
IType testClass= cu.createType(
"public class Test1 extends A implements B {\n"
+ " @Deprecated\n"
+ " java.util.List<String>[][] getArray() throws RuntimeException {\n"
+ " return (ArrayList<String>[][]) new ArrayList<?>[1][2];\n"
+ " }\n"
+ "}\n", null, true, null);
cu.createImport("java.util.ArrayList", null, null);
RefactoringASTParser parser= new RefactoringASTParser(astLevel);
CompilationUnit unit= parser.parse(cu, true);
AbstractTypeDeclaration declaration= ASTNodes.getParent(NodeFinder.perform(unit, testClass.getNameRange()), AbstractTypeDeclaration.class);
assertNotNull("Could not find type declaration node", declaration);
ITypeBinding binding= declaration.resolveBinding();
assertNotNull("Binding for type declaration could not be resolved", binding);
IMethodBinding[] overridableMethods= StubUtility2Core.getOverridableMethods(unit.getAST(), binding, false);
AddUnimplementedMethodsOperation op= new AddUnimplementedMethodsOperation(unit, binding, overridableMethods, -1, true, true, true);
op.run(new NullProgressMonitor());
IMethod[] methods= testClass.getMethods();
checkMethods(new String[] { "a", "b", "c", "getArray", "equals", "clone", "toString", "finalize", "hashCode" }, methods);
IImportDeclaration[] imports= cu.getImports();
checkImports(new String[] { "java.util.Date", "java.util.Hashtable", "java.util.Vector", "java.util.ArrayList" }, imports);
IProblem[] problems= parser.parse(cu, true).getProblems();
assertArrayEquals(new IProblem[0], problems);
}
private void testHelper(IType testClass) throws JavaModelException, CoreException {
testHelper(testClass, -1, true);
}
private void testHelper(IType testClass, int insertionPos, boolean implementAllOverridable) throws JavaModelException, CoreException {
RefactoringASTParser parser= new RefactoringASTParser(IASTSharedValues.SHARED_AST_LEVEL);
CompilationUnit unit= parser.parse(testClass.getCompilationUnit(), true);
AbstractTypeDeclaration declaration= ASTNodes.getParent(NodeFinder.perform(unit, testClass.getNameRange()), AbstractTypeDeclaration.class);
assertNotNull("Could not find type declaration node", declaration);
ITypeBinding binding= declaration.resolveBinding();
assertNotNull("Binding for type declaration could not be resolved", binding);
IMethodBinding[] overridableMethods= implementAllOverridable ? StubUtility2Core.getOverridableMethods(unit.getAST(), binding, false) : null;
AddUnimplementedMethodsOperation op= new AddUnimplementedMethodsOperation(unit, binding, overridableMethods, insertionPos, true, true, true);
op.run(new NullProgressMonitor());
JavaModelUtil.reconcile(testClass.getCompilationUnit());
}
private void checkMethodsInOrder(String[] expected, IMethod[] methods) {
String[] actualNames= new String[methods.length];
for (int i= 0; i < actualNames.length; i++) {
actualNames[i]= methods[i].getElementName();
}
assertEquals(Strings.concatenate(expected, ", "), Strings.concatenate(actualNames, ", "));
}
private void checkMethods(String[] expected, IMethod[] methods) {
int nMethods= methods.length;
int nExpected= expected.length;
assertEquals("" + nExpected + " methods expected, is " + nMethods, nExpected, nMethods);
for (int i= 0; i < nExpected; i++) {
String methName= expected[i];
assertTrue("method " + methName + " expected", nameContained(methName, methods));
}
}
private void checkImports(String[] expected, IImportDeclaration[] imports) {
int nImports= imports.length;
int nExpected= expected.length;
if (nExpected != nImports) {
StringBuilder buf= new StringBuilder();
buf.append(nExpected).append(" imports expected, is ").append(nImports).append("\n");
buf.append("expected:\n");
for (String e : expected) {
buf.append(e).append("\n");
}
buf.append("actual:\n");
for (IImportDeclaration i : imports) {
buf.append(i).append("\n");
}
fail(buf.toString());
}
for (int i= 0; i < nExpected; i++) {
String impName= expected[i];
assertTrue("import " + impName + " expected", nameContained(impName, imports));
}
}
private boolean nameContained(String methName, IJavaElement[] methods) {
for (IJavaElement method : methods) {
if (method.getElementName().equals(methName)) {
return true;
}
}
return false;
}
}