| /******************************************************************************* |
| * Copyright (c) 2000, 2021 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: |
| * Jesper Kamstrup Linnet (eclipse@kamstrup-linnet.dk) - initial API and implementation |
| * (report 36180: Callers/Callees view) |
| *******************************************************************************/ |
| package org.eclipse.jdt.ui.tests.callhierarchy; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.List; |
| |
| import org.junit.Assert; |
| |
| import org.eclipse.jdt.testplugin.JavaProjectHelper; |
| |
| import org.eclipse.core.runtime.CoreException; |
| |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IncrementalProjectBuilder; |
| |
| import org.eclipse.jdt.core.ICompilationUnit; |
| 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.internal.corext.callhierarchy.MethodWrapper; |
| |
| import org.eclipse.jdt.ui.tests.core.rules.Java17ProjectTestSetup; |
| import org.eclipse.jdt.ui.tests.core.rules.ProjectTestSetup; |
| |
| @SuppressWarnings("javadoc") |
| public class CallHierarchyTestHelper { |
| private static final String[] EMPTY= new String[0]; |
| |
| private IJavaProject fJavaProject1; |
| private IJavaProject fJavaProject2; |
| private IJavaProject fJavaProject3; |
| private IType fType1; |
| private IType fType2; |
| private IType fTypeP; |
| private IType fFooImplAType; |
| private IType fFooImplBType; |
| private IType fFooType; |
| private IType fAbsType; |
| private IType fAbsI1Type; |
| private IType fAbsI2Type; |
| private IPackageFragment fPack1; |
| private IPackageFragment fPack2; |
| private IPackageFragment fPack3; |
| |
| private IMethod fMethod1; |
| private IMethod fMethod2; |
| private IMethod fMethod3; |
| private IMethod fMethod4; |
| private IMethod fRecursiveMethod1; |
| private IMethod fRecursiveMethod2; |
| private IMethod fCalleeMethod; |
| private IMethod fAbsCalleeMethod; |
| private IMethod fFooMethod; |
| private IMethod fFooImplMethod_A; |
| private IMethod fFooImplMethod_B; |
| private IMethod fAbsFooMethod; |
| private IMethod fAbsI1FooMethod; |
| private IMethod fAbsI2FooMethod; |
| |
| public void setUp() throws Exception { |
| fJavaProject1= JavaProjectHelper.createJavaProject("TestProject1", "bin"); |
| fJavaProject2= JavaProjectHelper.createJavaProject("TestProject2", "bin"); |
| fJavaProject3= JavaProjectHelper.createJavaProject("TestProject3", "bin"); |
| assertBuildWithoutErrors(fJavaProject1); |
| assertBuildWithoutErrors(fJavaProject2); |
| assertBuildWithoutErrors(fJavaProject3); |
| fType1= null; |
| fType2= null; |
| fTypeP= null; |
| fFooImplAType= null; |
| fFooImplBType= null; |
| fAbsI1Type= null; |
| fAbsI2Type= null; |
| fAbsType= null; |
| fFooType= null; |
| fPack1= null; |
| fPack2= null; |
| fPack3= null; |
| } |
| |
| public void tearDown() throws Exception { |
| JavaProjectHelper.delete(fJavaProject1); |
| JavaProjectHelper.delete(fJavaProject2); |
| JavaProjectHelper.delete(fJavaProject3); |
| } |
| |
| |
| /** |
| * Creates two simple classes, A and B. Sets the instance fields fType1 and fType2. |
| */ |
| public void createSimpleClasses() throws Exception { |
| createPackages(); |
| |
| |
| ICompilationUnit cu1= fPack1.getCompilationUnit("A.java"); |
| |
| fType1= |
| cu1.createType( |
| "public class A {\n" + |
| "public A() {\n" + |
| "}\n " + |
| "public void method1() {\n" + |
| "}\n " + |
| "public void method2() {\n" + |
| " method1();\n" + |
| "}\n " + |
| "public void recursiveMethod1() {\n" + |
| " recursiveMethod2();\n " + |
| "}\n " + |
| "public void recursiveMethod2() {\n" + |
| " recursiveMethod1();\n " + |
| "}\n" + |
| "}\n", |
| null, |
| true, |
| null); |
| |
| ICompilationUnit cu2= fPack2.getCompilationUnit("B.java"); |
| fType2= |
| cu2.createType( |
| "public class B extends pack1.A {\npublic void method3() { method1(); method2(); }\n public void method4() { method3(); }\n}\n", |
| null, |
| true, |
| null); |
| |
| assertBuildWithoutErrors(fJavaProject1); |
| assertBuildWithoutErrors(fJavaProject2); |
| } |
| |
| /** |
| * Creates two simple classes, A and its subclass B, where B calls A's implicit constructor explicitly. Sets the instance fields fType1 and fType2. |
| */ |
| public void createImplicitConstructorClasses() throws Exception { |
| createPackages(); |
| |
| ICompilationUnit cu1= fPack1.getCompilationUnit("A.java"); |
| fType1= |
| cu1.createType( |
| "public class A {\n public void method1() { }\n public void method2() { method1(); }\n public void recursiveMethod1() { recursiveMethod2(); }\n public void recursiveMethod2() { recursiveMethod1(); }\n}\n", |
| null, |
| true, |
| null); |
| |
| ICompilationUnit cu2= fPack2.getCompilationUnit("B.java"); |
| fType2= |
| cu2.createType( |
| "public class B extends pack1.A {\n public B(String name) { super(); }\n}\n", |
| null, |
| true, |
| null); |
| assertBuildWithoutErrors(fPack1); |
| } |
| |
| /** |
| * Creates an inner class and sets the class attribute fType1. |
| */ |
| public void createInnerClass() throws Exception { |
| createPackages(); |
| |
| ICompilationUnit cu1= fPack1.getCompilationUnit("Outer.java"); |
| fType1= |
| cu1.createType( |
| "public class Outer {\n" + |
| "private Inner inner= new Inner();\n" + |
| "class Inner { public void innerMethod1() { outerMethod1(); }\n public void innerMethod2() { innerMethod1(); }\n }\n" + |
| "public void outerMethod1() { }\n public void outerMethod2() { inner.innerMethod2(); }\n" + |
| "}", |
| null, |
| true, |
| null); |
| assertBuildWithoutErrors(fPack1); |
| } |
| |
| /** |
| * Creates an anonymous inner class and sets the class attribute fType1. |
| */ |
| public void createAnonymousInnerClass() throws Exception { |
| createPackages(); |
| |
| ICompilationUnit cu1= fPack1.getCompilationUnit("AnonymousInner.java"); |
| fType1= |
| cu1.createType( |
| "public class AnonymousInner {\n" + |
| " Object anonClass = new Object() {\n" + |
| " void anotherMethod() {\n" + |
| " someMethod();\n" + |
| " }\n" + |
| " };\n" + |
| " void someMethod() {\n" + |
| " }\n" + |
| "}\n", |
| null, |
| true, |
| null); |
| |
| ICompilationUnit cu2= fPack2.getCompilationUnit("Outer.java"); |
| fType2= |
| cu2.createType( |
| "public class Outer {\n" + |
| " interface Intf {\n" + |
| " public void foo();\n" + |
| " }\n" + |
| " class Clazz {\n" + |
| " public void foo() { };\n" + |
| " }\n" + |
| " public void anonymousOnInterface() {\n" + |
| " new Intf() {\n"+ |
| " public void foo() {\n"+ |
| " someMethod();\n"+ |
| " }\n"+ |
| " };\n"+ |
| " }\n" + |
| " public void anonymousOnClass() {\n" + |
| " new Clazz() {\n"+ |
| " public void foo() {\n"+ |
| " someMethod();\n"+ |
| " }\n"+ |
| " };\n"+ |
| " }\n" + |
| " public void someMethod() { }\n"+ |
| "}\n", |
| null, |
| true, |
| null); |
| assertBuildWithoutErrors(fPack1); |
| } |
| |
| /** |
| * Creates an anonymous inner class inside another method and sets the class attribute fType1. |
| */ |
| public void createAnonymousInnerClassInsideMethod() throws Exception { |
| createPackages(); |
| |
| ICompilationUnit cu1= fPack1.getCompilationUnit("AnonymousInnerInsideMethod.java"); |
| fType1= |
| cu1.createType( |
| "public class AnonymousInnerInsideMethod {\n" + |
| " void m() {\n" + |
| " System.out.println(\"before\");\n"+ |
| " Runnable runnable = new Runnable() {\n"+ |
| " public void run() {\n"+ |
| " System.out.println(\"run\");\n"+ |
| " }\n"+ |
| " };\n"+ |
| " runnable.run();\n"+ |
| " }\n"+ |
| "}\n", |
| null, |
| true, |
| null); |
| assertBuildWithoutErrors(fPack1); |
| } |
| |
| /** |
| * Creates a class with various lambda function definitions and calls |
| */ |
| public void createClassWithLambdaCalls() throws Exception { |
| createPackages(); |
| |
| ICompilationUnit cu= fPack1.getCompilationUnit("Snippet.java"); |
| fType1= cu.createType("public class Snippet {\n" |
| + " static Function<? super String, ? extends String> mapper1 = y -> transform(y);\n" |
| + " Function<? super String, ? extends String> mapper2 = y -> transform(y);\n" |
| + "\n" |
| + " static {\n" |
| + " mapper1 = y -> transform(y);\n" |
| + " }\n" |
| + "\n" |
| + " public Snippet() {\n" |
| + " mapper2 = y -> transform(y);\n" |
| + " }\n" |
| + "\n" |
| + " public static void main(String[] args) {\n" |
| + " mapper1 = y -> transform(y);\n" |
| + " }\n" |
| + "\n" |
| + " Object[] funcCall() {\n" |
| + " return List.of(\"aaa\").stream().map(y -> transform(y)).toArray();\n" |
| + " }\n" |
| + "\n" |
| + " static String transform(String s) {\n" |
| + " x();\n" |
| + " return s.toUpperCase();\n" |
| + " }\n" |
| + " static String x() {" |
| + " return null;\n" |
| + "}\n" |
| + "}", |
| null, |
| true, |
| null); |
| cu.createImport("java.util.List", fType1, null); |
| cu.createImport("java.util.function.Function", fType1, null); |
| fMethod1= fType1.getMethod("x", EMPTY); |
| fMethod2= fType1.getMethod("transform", new String[] { "QString;" }); |
| Assert.assertNotNull(fMethod1); |
| Assert.assertNotNull(fMethod2); |
| assertBuildWithoutErrors(fPack1); |
| } |
| |
| /** |
| * Creates a class with a static initializer and sets the class attribute fType1. |
| */ |
| public void createStaticInitializerClass() throws Exception { |
| createPackages(); |
| |
| ICompilationUnit cu1= fPack1.getCompilationUnit("Initializer.java"); |
| fType1= |
| cu1.createType( |
| "public class Initializer { static { someMethod(); }\n public static void someMethod() { }\n }\n", |
| null, |
| true, |
| null); |
| assertBuildWithoutErrors(fPack1); |
| } |
| |
| /** |
| * Creates a record class, OneRecord and sets the instance field fType1. |
| */ |
| public void createRecordClasses() throws Exception { |
| ProjectTestSetup projectsetup= new Java17ProjectTestSetup(false); |
| fJavaProject3.setRawClasspath(projectsetup.getDefaultClasspath(), null); |
| JavaProjectHelper.set17CompilerOptions(fJavaProject3, false); |
| |
| IPackageFragmentRoot fSourceFolder= JavaProjectHelper.addSourceContainer(fJavaProject3, "src"); |
| |
| String MODULE_INFO_FILE_CONTENT = "" |
| + "module test {\n" |
| + "}\n"; |
| |
| IPackageFragment def= fSourceFolder.createPackageFragment("", false, null); |
| def.createCompilationUnit("module-info.java", MODULE_INFO_FILE_CONTENT, false, null); |
| |
| fPack3= fSourceFolder.createPackageFragment("test", false, null); |
| |
| JavaProjectHelper.set16CompilerOptions(fJavaProject1, true); |
| |
| ICompilationUnit cu1= fPack3.getCompilationUnit("Outer.java"); |
| |
| fType1= |
| cu1.createType( |
| "public class Outer {\n" + |
| "record OneRecord(int number1, int number2) {\n" + |
| " OneRecord() { this(1, 2); }\n" + |
| "}\n " + |
| " public static void method1() {\n" + |
| " new OneRecord(3, 5);\n" + |
| " }\n" + |
| " public static void method2() {\n" + |
| " new OneRecord();\n" + |
| " }\n" + |
| " public static void method3() {\n" + |
| " method1();\n" + |
| " method2();\n" + |
| " }\n" + |
| "}\n", |
| null, |
| true, |
| null); |
| assertBuildWithoutErrors(fJavaProject3); |
| } |
| |
| public void createCalleeClasses() throws Exception { |
| createPackages(); |
| |
| ICompilationUnit cu3= fPack2.getCompilationUnit("P.java"); |
| fTypeP= |
| cu3.createType( |
| "public class P {\n" |
| + " private A handler;\n" |
| + " private Abs absHandler;\n" |
| + "\n" |
| + " public void callFoo() {\n" |
| + " handler.foo();\n" |
| + " }\n" |
| + " public void callAbsFoo() {\n" |
| + " absHandler.absFoo();\n" |
| + " }\n" |
| + "\n" |
| + "}", |
| null, |
| true, |
| null); |
| |
| ICompilationUnit cu4= fPack2.getCompilationUnit("A.java"); |
| fFooType= cu4.createType( |
| "public interface A {\n" |
| + " void foo();\n" |
| + "}\n" |
| , null, true, null); |
| |
| |
| ICompilationUnit cu5= fPack2.getCompilationUnit("AImpl.java"); |
| fFooImplAType= cu5.createType( |
| "public class AImpl implements A {\n" |
| + " public void foo() {\n" |
| + " System.out.println();\n" |
| + " }\n" |
| + "}\n" |
| , null, true, null); |
| |
| ICompilationUnit cu6= fPack2.getCompilationUnit("BImpl.java"); |
| fFooImplBType= cu6.createType( |
| "public class BImpl implements A {\n" |
| + " public void foo() {\n" |
| + " System.out.printf(\"\");\n" |
| + " }\n" |
| + "}\n" |
| , null, true, null); |
| |
| |
| ICompilationUnit cu7= fPack2.getCompilationUnit("Abs.java"); |
| fAbsType= cu7.createType( |
| "public abstract class Abs {\n" |
| + " abstract void absFoo();\n" |
| + "}\n" |
| , null, true, null); |
| |
| ICompilationUnit cu8= fPack2.getCompilationUnit("AbsI1.java"); |
| fAbsI1Type= cu8.createType( |
| "public class AbsI1 extends Abs {\n" |
| + " void absFoo() {}\n" |
| + "}\n" |
| , null, true, null); |
| |
| ICompilationUnit cu9= fPack2.getCompilationUnit("AbsI2.java"); |
| fAbsI2Type= cu9.createType( |
| "public class AbsI2 extends Abs {\n" |
| + " void absFoo() {}\n" |
| + "}\n" |
| , null, true, null); |
| |
| assertBuildWithoutErrors(fJavaProject1); |
| assertBuildWithoutErrors(fJavaProject2); |
| |
| } |
| |
| /** |
| * Creates two packages (pack1 and pack2) in different projects. Sets the |
| * instance fields fPack1 and fPack2. |
| */ |
| public void createPackages() throws Exception { |
| JavaProjectHelper.addRTJar9(fJavaProject1); |
| |
| IPackageFragmentRoot root1= JavaProjectHelper.addSourceContainer(fJavaProject1, "src"); |
| fPack1= root1.createPackageFragment("pack1", true, null); |
| assertBuildWithoutErrors(fPack1); |
| |
| JavaProjectHelper.addRTJar9(fJavaProject2); |
| JavaProjectHelper.addRequiredProject(fJavaProject2, fJavaProject1); |
| |
| IPackageFragmentRoot root2= JavaProjectHelper.addSourceContainer(fJavaProject2, "src"); |
| fPack2= root2.createPackageFragment("pack2", true, null); |
| assertBuildWithoutErrors(fPack2); |
| } |
| |
| /** |
| * Returns all error markers on given resource, recursively |
| */ |
| public List<IMarker> getErrorMarkers(IResource resource) throws CoreException { |
| IMarker[] markers = resource.findMarkers(null, true, IResource.DEPTH_INFINITE); |
| List<IMarker> errorMarkers = new ArrayList<>(); |
| for (IMarker marker : markers) { |
| if (marker.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO) == IMarker.SEVERITY_ERROR) { |
| errorMarkers.add(marker); |
| } |
| } |
| return errorMarkers; |
| } |
| |
| public static List<String> convertMarkers(IMarker [] markers) throws Exception { |
| List<String> result = new ArrayList<>(); |
| for (int i = 0; i < markers.length; i++) { |
| IMarker marker = markers[i]; |
| StringBuilder sb = new StringBuilder("Marker #"); |
| sb.append(i).append("["); |
| sb.append(marker.getAttribute("message", null)); |
| sb.append(" at line: ").append(marker.getAttribute("lineNumber", 0)); |
| sb.append("], "); |
| result.add(sb.toString()); |
| } |
| return result; |
| } |
| |
| /** |
| * Verifies that no error markers exist in the given resource. |
| * <p> |
| * |
| * @param element |
| * The resource that is searched for error markers |
| */ |
| protected void assertBuildWithoutErrors(IJavaElement element) throws Exception { |
| IResource resource= element.getResource(); |
| Assert.assertNotNull("Given element has no resource: " + element, resource); |
| resource.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null); |
| assertNoErrorMarkers(resource); |
| } |
| |
| /** |
| * Verifies that no error markers exist in the given resource. |
| * <p> |
| * |
| * @param resource |
| * The resource that is searched for error markers |
| */ |
| protected void assertNoErrorMarkers(IResource resource) throws Exception { |
| List<IMarker> errorMarkers = getErrorMarkers(resource); |
| List<String> messages = convertMarkers(errorMarkers.toArray(new IMarker[errorMarkers.size()])); |
| Assert.assertEquals("No error marker expected, but found markers with messages: " + messages.toString(), 0, |
| errorMarkers.size()); |
| } |
| |
| /** |
| * Asserts that all the expected methods were found in the call results. |
| */ |
| public void assertCalls(Collection<IMember> expectedMembers, Collection<?> calls) { |
| Collection<IMember> foundMembers= new ArrayList<>(); |
| |
| for (MethodWrapper element : (Collection<MethodWrapper>)calls) { |
| foundMembers.add(element.getMember()); |
| } |
| |
| Assert.assertEquals("Wrong number of calls", expectedMembers.size(), calls.size()); |
| Assert.assertTrue("One or more members not found", foundMembers.containsAll(expectedMembers)); |
| } |
| |
| /** |
| * Asserts that all the expected methods were found in the call results. |
| */ |
| public void assertCalls(Collection<IMember> expectedMembers, MethodWrapper[] callResults) { |
| assertCalls(expectedMembers, Arrays.asList(callResults)); |
| } |
| |
| /** |
| * Asserts that all the expected methods were found in the call results. |
| */ |
| public void assertCalls(IMember[] expectedMembers, Object[] callResults) { |
| assertCalls(Arrays.asList(expectedMembers), Arrays.asList(callResults)); |
| } |
| |
| public MethodWrapper findMethodWrapper(IMethod method, Object[] methodWrappers) { |
| MethodWrapper thirdLevelMethodWrapper= null; |
| for (Object methodWrapper : methodWrappers) { |
| if (method.equals(((MethodWrapper) methodWrapper).getMember())) { |
| thirdLevelMethodWrapper= (MethodWrapper) methodWrapper; |
| break; |
| } |
| } |
| return thirdLevelMethodWrapper; |
| } |
| |
| /** |
| * @return |
| */ |
| public IJavaProject getJavaProject2() { |
| return fJavaProject2; |
| } |
| |
| /** |
| * @return |
| */ |
| public IPackageFragment getPackage1() { |
| return fPack1; |
| } |
| |
| /** |
| * @return |
| */ |
| public IPackageFragment getPackage2() { |
| return fPack2; |
| } |
| |
| /** |
| * @return |
| */ |
| public IType getType1() { |
| return fType1; |
| } |
| |
| /** |
| * @return |
| */ |
| public IType getType2() { |
| return fType2; |
| } |
| |
| public IType getTypeP() { |
| return fTypeP; |
| } |
| |
| public IType getFooImplAType() { |
| return fFooImplAType; |
| } |
| |
| public IType getFooImplBType() { |
| return fFooImplBType; |
| } |
| |
| public IType getFooType() { |
| return fFooType; |
| } |
| |
| public IType getAbsType() { |
| return fAbsType; |
| } |
| |
| public IType getAbsI1Type() { |
| return fAbsI1Type; |
| } |
| |
| public IType getAbsI2Type() { |
| return fAbsI2Type; |
| } |
| |
| public IMethod getMethod1() { |
| if (fMethod1 == null) { |
| fMethod1= getType1().getMethod("method1", EMPTY); |
| } |
| return fMethod1; |
| } |
| |
| public IMethod getMethod2() { |
| if (fMethod2 == null) { |
| fMethod2= getType1().getMethod("method2", EMPTY); |
| } |
| return fMethod2; |
| } |
| |
| public IMethod getMethod3() { |
| if (fMethod3 == null) { |
| fMethod3= getType2().getMethod("method3", EMPTY); |
| } |
| return fMethod3; |
| } |
| |
| public IMethod getMethod4() { |
| if (fMethod4 == null) { |
| fMethod4= getType2().getMethod("method4", EMPTY); |
| } |
| return fMethod4; |
| } |
| |
| public IMethod getRecursiveMethod1() { |
| if (fRecursiveMethod1 == null) { |
| fRecursiveMethod1= getType1().getMethod("recursiveMethod1", EMPTY); |
| } |
| return fRecursiveMethod1; |
| } |
| |
| public IMethod getRecursiveMethod2() { |
| if (fRecursiveMethod2 == null) { |
| fRecursiveMethod2= getType1().getMethod("recursiveMethod2", EMPTY); |
| } |
| return fRecursiveMethod2; |
| } |
| |
| public IMethod getCalleeMethod() { |
| if (fCalleeMethod == null) { |
| fCalleeMethod= getTypeP().getMethod("callFoo", EMPTY); |
| } |
| return fCalleeMethod; |
| } |
| |
| public IMethod getAbsCalleeMethod() { |
| if (fAbsCalleeMethod == null) { |
| fAbsCalleeMethod= getTypeP().getMethod("callAbsFoo", EMPTY); |
| } |
| return fAbsCalleeMethod; |
| } |
| |
| public IMethod getFooImplMethod_A() { |
| if (fFooImplMethod_A == null) { |
| fFooImplMethod_A= getFooImplAType().getMethod("foo", EMPTY); |
| } |
| return fFooImplMethod_A; |
| } |
| |
| public IMethod getFooImplMethod_B() { |
| if (fFooImplMethod_B == null) { |
| fFooImplMethod_B= getFooImplBType().getMethod("foo", EMPTY); |
| } |
| return fFooImplMethod_B; |
| } |
| |
| public IMethod getFooMethod() { |
| if (fFooMethod == null) { |
| fFooMethod= getFooType().getMethod("foo", EMPTY); |
| } |
| return fFooMethod; |
| } |
| |
| public IMethod getAbsFooMethod() { |
| if (fAbsFooMethod == null) { |
| fAbsFooMethod= getAbsType().getMethod("absFoo", EMPTY); |
| } |
| return fAbsFooMethod; |
| } |
| |
| public IMethod getAbsI1FooMethod() { |
| if (fAbsI1FooMethod == null) { |
| fAbsI1FooMethod= getAbsI1Type().getMethod("absFoo", EMPTY); |
| } |
| return fAbsI1FooMethod; |
| } |
| |
| public IMethod getAbsI2FooMethod() { |
| if (fAbsI2FooMethod == null) { |
| fAbsI2FooMethod= getAbsI2Type().getMethod("absFoo", EMPTY); |
| } |
| return fAbsI2FooMethod; |
| } |
| } |