blob: 2c117ed7d34e7ba0b4e06034eb7983c12ccbf2be [file] [log] [blame]
// ***************************************************************************************************************************
// * 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.apache.juneau.reflection;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
import org.apache.juneau.*;
import org.apache.juneau.annotation.*;
import org.apache.juneau.reflect.*;
import org.apache.juneau.svl.*;
import org.junit.*;
public class MethodInfoTest {
@Documented
@Target({METHOD,TYPE})
@Retention(RUNTIME)
@Inherited
public static @interface A {
String value();
}
@Documented
@Target({METHOD,TYPE})
@Retention(RUNTIME)
@Inherited
public static @interface AX {
String value();
}
@Documented
@Target({METHOD,TYPE})
@Retention(RUNTIME)
@Inherited
@PropertyStoreApply(AConfigApply.class)
public static @interface AConfig {
String value();
}
public static class AConfigApply extends ConfigApply<AConfig> {
protected AConfigApply(Class<AConfig> c, VarResolverSession r) {
super(c, r);
}
@Override
public void apply(AnnotationInfo<AConfig> a, PropertyStoreBuilder ps) {
}
}
private static void check(String expected, Object o) {
assertEquals(expected, TO_STRING.apply(o));
}
private static final Function<Object,String> TO_STRING = new Function<Object,String>() {
@SuppressWarnings({ "rawtypes" })
@Override
public String apply(Object t) {
if (t == null)
return null;
if (t instanceof MethodInfo)
return ((MethodInfo)t).getDeclaringClass().getSimpleName() + '.' + ((MethodInfo)t).getShortName();
if (t instanceof Method)
return ((Method)t).getDeclaringClass().getSimpleName() + '.' + MethodInfo.of((Method)t).getShortName();
if (t instanceof List)
return ((List<?>)t).stream().map(this).collect(Collectors.joining(","));
if (t instanceof AnnotationInfo)
return apply(((AnnotationInfo)t).getAnnotation());
if (t instanceof A)
return "@A(" + ((A)t).value() + ")";
if (t instanceof PA)
return "@PA(" + ((PA)t).value() + ")";
if (t instanceof AConfig)
return "@AConfig(" + ((AConfig)t).value() + ")";
if (t instanceof AnnotationList) {
AnnotationList al = (AnnotationList)t;
return al.toString();
}
if (t instanceof ClassInfo)
return ((ClassInfo)t).getSimpleName();
return t.toString();
}
};
private static MethodInfo ofm(Class<?> c, String name, Class<?>...pt) {
try {
return MethodInfo.of(c.getDeclaredMethod(name, pt));
} catch (NoSuchMethodException | SecurityException e) {
fail(e.getLocalizedMessage());
}
return null;
}
//-----------------------------------------------------------------------------------------------------------------
// Instantiation.
//-----------------------------------------------------------------------------------------------------------------
public static class A1 {
public void m() {}
}
static MethodInfo a_m = ofm(A1.class, "m");
@Test
public void of_withDeclaringClass() {
check("A1.m()", a_m);
check("A1.m()", MethodInfo.of(ClassInfo.of(A1.class), a_m.inner(), null));
}
@Test
public void of_withoutDeclaringClass() {
MethodInfo mi = MethodInfo.of(a_m.inner());
check("A1.m()", mi);
}
@Test
public void of_null() {
check(null, MethodInfo.of(null));
check(null, MethodInfo.of((ClassInfo)null, null, null));
}
//-----------------------------------------------------------------------------------------------------------------
// Matching methods.
//-----------------------------------------------------------------------------------------------------------------
public static interface B1 {
public int foo(int x);
public int foo(String x);
public int foo();
}
public static class B2 {
public int foo(int x) { return 0; }
public int foo(String x) {return 0;}
public int foo() {return 0;}
}
public static class B3 extends B2 implements B1 {
@Override
public int foo(int x) {return 0;}
@Override
public int foo(String x) {return 0;}
@Override
public int foo() {return 0;}
}
@Test
public void findMatchingMethods() throws Exception {
MethodInfo mi = MethodInfo.of(B3.class.getMethod("foo", int.class));
check("B3.foo(int),B2.foo(int),B1.foo(int)", mi.getMatching());
}
//-----------------------------------------------------------------------------------------------------------------
// Annotations
//-----------------------------------------------------------------------------------------------------------------
@A("C1")
public static interface C1 {
@A("a1") void a1();
@A("a2a") void a2();
@A("a3") void a3(CharSequence foo);
void a4();
void a5();
}
@A("C2")
public static class C2 implements C1 {
@Override public void a1() {}
@Override @A("a2b") public void a2() {}
@Override public void a3(CharSequence s) {}
@Override public void a4() {}
@Override public void a5() {}
}
@A("C3")
public static class C3 extends C2 {
@Override public void a1() {}
@Override public void a2() {}
@Override public void a3(CharSequence foo) {}
@Override @A("a4") public void a4() {}
@Override public void a5() {}
}
static MethodInfo
c_a1 = ofm(C3.class, "a1"),
c_a2 = ofm(C3.class, "a2"),
c_a3 = ofm(C3.class, "a3", CharSequence.class),
c_a4 = ofm(C3.class, "a4"),
c_a5 = ofm(C3.class, "a5");
@Test
public void getAnnotations() {
check("@A(a1),@A(C3),@A(C2),@A(C1)", c_a1.getAnnotations(A.class));
check("@A(a2b),@A(a2a),@A(C3),@A(C2),@A(C1)", c_a2.getAnnotations(A.class));
check("@A(a3),@A(C3),@A(C2),@A(C1)", c_a3.getAnnotations(A.class));
check("@A(a4),@A(C3),@A(C2),@A(C1)", c_a4.getAnnotations(A.class));
check("@A(C3),@A(C2),@A(C1)", c_a5.getAnnotations(A.class));
}
@Test
public void getAnnotations_notExistent() {
check("", c_a1.getAnnotations(AX.class));
check("", c_a2.getAnnotations(AX.class));
check("", c_a3.getAnnotations(AX.class));
check("", c_a4.getAnnotations(AX.class));
check("", c_a5.getAnnotations(AX.class));
}
@Test
public void getAnnotationsParentFirst() {
check("@A(C1),@A(C2),@A(C3),@A(a1)", c_a1.getAnnotationsParentFirst(A.class));
check("@A(C1),@A(C2),@A(C3),@A(a2a),@A(a2b)", c_a2.getAnnotationsParentFirst(A.class));
check("@A(C1),@A(C2),@A(C3),@A(a3)", c_a3.getAnnotationsParentFirst(A.class));
check("@A(C1),@A(C2),@A(C3),@A(a4)", c_a4.getAnnotationsParentFirst(A.class));
check("@A(C1),@A(C2),@A(C3)", c_a5.getAnnotationsParentFirst(A.class));
}
@Test
public void appendAnnotations() {
check("@A(a1),@A(C3),@A(C2),@A(C1)", c_a1.appendAnnotations(new ArrayList<>(), A.class));
check("@A(a2b),@A(a2a),@A(C3),@A(C2),@A(C1)", c_a2.appendAnnotations(new ArrayList<>(), A.class));
check("@A(a3),@A(C3),@A(C2),@A(C1)", c_a3.appendAnnotations(new ArrayList<>(), A.class));
check("@A(a4),@A(C3),@A(C2),@A(C1)", c_a4.appendAnnotations(new ArrayList<>(), A.class));
check("@A(C3),@A(C2),@A(C1)", c_a5.appendAnnotations(new ArrayList<>(), A.class));
}
@Test
public void appendAnnotationsParentFirst() {
check("@A(C1),@A(C2),@A(C3),@A(a1)", c_a1.appendAnnotationsParentFirst(new ArrayList<>(), A.class));
check("@A(C1),@A(C2),@A(C3),@A(a2a),@A(a2b)", c_a2.appendAnnotationsParentFirst(new ArrayList<>(), A.class));
check("@A(C1),@A(C2),@A(C3),@A(a3)", c_a3.appendAnnotationsParentFirst(new ArrayList<>(), A.class));
check("@A(C1),@A(C2),@A(C3),@A(a4)", c_a4.appendAnnotationsParentFirst(new ArrayList<>(), A.class));
check("@A(C1),@A(C2),@A(C3)", c_a5.appendAnnotationsParentFirst(new ArrayList<>(), A.class));
}
@Test
public void getAnnotation() {
check("@A(a1)", c_a1.getAnnotation(A.class));
check("@A(a2b)", c_a2.getAnnotation(A.class));
check("@A(a3)", c_a3.getAnnotation(A.class));
check("@A(a4)", c_a4.getAnnotation(A.class));
check(null, c_a5.getAnnotation(A.class));
}
@Test
public void getAnnotationAny() {
check("@A(a1)", c_a1.getAnyAnnotation(AX.class, A.class));
check("@A(a2b)", c_a2.getAnyAnnotation(AX.class, A.class));
check("@A(a3)", c_a3.getAnyAnnotation(AX.class, A.class));
check("@A(a4)", c_a4.getAnyAnnotation(AX.class, A.class));
check(null, c_a5.getAnyAnnotation(AX.class, A.class));
}
@Test
public void getAnnotationsMap() {
check("@A(C3),@A(C2),@A(a1),@A(C1),@PA(10)", c_a1.getAnnotationList(null));
check("@A(C3),@A(a2b),@A(C2),@A(a2a),@A(C1),@PA(10)", c_a2.getAnnotationList(null));
check("@A(C3),@A(C2),@A(a3),@A(C1),@PA(10)", c_a3.getAnnotationList(null));
check("@A(a4),@A(C3),@A(C2),@A(C1),@PA(10)", c_a4.getAnnotationList(null));
check("@A(C3),@A(C2),@A(C1),@PA(10)", c_a5.getAnnotationList(null));
}
@Test
public void getAnnotationsMapParentFirst() {
check("@PA(10),@A(C1),@A(a1),@A(C2),@A(C3)", c_a1.getAnnotationListParentFirst(null));
check("@PA(10),@A(C1),@A(a2a),@A(C2),@A(a2b),@A(C3)", c_a2.getAnnotationListParentFirst(null));
check("@PA(10),@A(C1),@A(a3),@A(C2),@A(C3)", c_a3.getAnnotationListParentFirst(null));
check("@PA(10),@A(C1),@A(C2),@A(C3),@A(a4)", c_a4.getAnnotationListParentFirst(null));
check("@PA(10),@A(C1),@A(C2),@A(C3)", c_a5.getAnnotationListParentFirst(null));
}
@A("C1") @AConfig("C1")
public static interface CB1 {
@A("a1") @AConfig("a1") void a1();
@A("a2a") @AConfig("a2a") void a2();
@A("a3") @AConfig("a3") void a3(CharSequence foo);
void a4();
void a5();
}
@A("C2") @AConfig("C2")
public static class CB2 implements CB1 {
@Override public void a1() {}
@Override @A("a2b") @AConfig("a2b") public void a2() {}
@Override public void a3(CharSequence s) {}
@Override public void a4() {}
@Override public void a5() {}
}
@A("C3") @AConfig("C3")
public static class CB3 extends CB2 {
@Override public void a1() {}
@Override public void a2() {}
@Override public void a3(CharSequence foo) {}
@Override @A("a4") @AConfig("a4") public void a4() {}
@Override public void a5() {}
}
static MethodInfo
cb_a1 = ofm(CB3.class, "a1"),
cb_a2 = ofm(CB3.class, "a2"),
cb_a3 = ofm(CB3.class, "a3", CharSequence.class),
cb_a4 = ofm(CB3.class, "a4"),
cb_a5 = ofm(CB3.class, "a5");
@Test
public void getConfigAnnotationsMap() {
check("@AConfig(C3),@AConfig(C2),@AConfig(a1),@AConfig(C1)", cb_a1.getAnnotationList(ConfigAnnotationFilter.INSTANCE));
check("@AConfig(C3),@AConfig(a2b),@AConfig(C2),@AConfig(a2a),@AConfig(C1)", cb_a2.getAnnotationList(ConfigAnnotationFilter.INSTANCE));
check("@AConfig(C3),@AConfig(C2),@AConfig(a3),@AConfig(C1)", cb_a3.getAnnotationList(ConfigAnnotationFilter.INSTANCE));
check("@AConfig(a4),@AConfig(C3),@AConfig(C2),@AConfig(C1)", cb_a4.getAnnotationList(ConfigAnnotationFilter.INSTANCE));
check("@AConfig(C3),@AConfig(C2),@AConfig(C1)", cb_a5.getAnnotationList(ConfigAnnotationFilter.INSTANCE));
}
@Test
public void getConfigAnnotationsMapParentFirst() {
check("@AConfig(C1),@AConfig(a1),@AConfig(C2),@AConfig(C3)", cb_a1.getAnnotationListParentFirst(ConfigAnnotationFilter.INSTANCE));
check("@AConfig(C1),@AConfig(a2a),@AConfig(C2),@AConfig(a2b),@AConfig(C3)", cb_a2.getAnnotationListParentFirst(ConfigAnnotationFilter.INSTANCE));
check("@AConfig(C1),@AConfig(a3),@AConfig(C2),@AConfig(C3)", cb_a3.getAnnotationListParentFirst(ConfigAnnotationFilter.INSTANCE));
check("@AConfig(C1),@AConfig(C2),@AConfig(C3),@AConfig(a4)", cb_a4.getAnnotationListParentFirst(ConfigAnnotationFilter.INSTANCE));
check("@AConfig(C1),@AConfig(C2),@AConfig(C3)", cb_a5.getAnnotationListParentFirst(ConfigAnnotationFilter.INSTANCE));
}
//-----------------------------------------------------------------------------------------------------------------
// Return type.
//-----------------------------------------------------------------------------------------------------------------
public static class D {
public void a1() {}
public Integer a2() {return null;}
}
static MethodInfo
d_a1 = ofm(D.class, "a1"),
d_a2 = ofm(D.class, "a2");
@Test
public void getReturnType() {
check("void", d_a1.getReturnType());
check("Integer", d_a2.getReturnType());
}
@Test
public void hasReturnType() {
assertTrue(d_a1.hasReturnType(void.class));
assertFalse(d_a1.hasReturnType(Integer.class));
assertTrue(d_a2.hasReturnType(Integer.class));
assertFalse(d_a2.hasReturnType(Number.class));
}
@Test
public void hasReturnTypeParent() {
assertTrue(d_a1.hasReturnTypeParent(void.class));
assertFalse(d_a1.hasReturnTypeParent(Integer.class));
assertTrue(d_a2.hasReturnTypeParent(Integer.class));
assertTrue(d_a2.hasReturnTypeParent(Number.class));
}
//-----------------------------------------------------------------------------------------------------------------
// Other methods
//-----------------------------------------------------------------------------------------------------------------
public static class E {
private String f;
public void a1(CharSequence foo) {
f = foo == null ? null : foo.toString();
}
public void a2(int f1, int f2) {}
public void a3() {}
}
static MethodInfo
e_a1 = ofm(E.class, "a1", CharSequence.class),
e_a2 = ofm(E.class, "a2", int.class, int.class),
e_a3 = ofm(E.class, "a3");
@Test
public void invoke() {
try {
E e = new E();
e_a1.invoke(e, "foo");
assertEquals("foo", e.f);
e_a1.invoke(e, (CharSequence)null);
assertNull(e.f);
} catch (Exception e) {
fail(e.getLocalizedMessage());
}
}
@Test
public void invokeFuzzy() {
try {
E e = new E();
e_a1.invokeFuzzy(e, "foo", 123);
assertEquals("foo", e.f);
e_a1.invokeFuzzy(e, 123, "bar");
assertEquals("bar", e.f);
} catch (Exception e) {
fail(e.getLocalizedMessage());
}
}
@Test
public void getSignature() {
assertEquals("a1(java.lang.CharSequence)", e_a1.getSignature());
assertEquals("a2(int,int)", e_a2.getSignature());
assertEquals("a3", e_a3.getSignature());
}
@Test
public void argsOnlyOfType() {
assertTrue(e_a1.argsOnlyOfType(CharSequence.class));
assertTrue(e_a1.argsOnlyOfType(CharSequence.class, Map.class));
assertFalse(e_a1.argsOnlyOfType());
}
public static class F {
public void isA() {}
public void is() {}
public void getA() {}
public void get() {}
public void setA() {}
public void set() {}
public void foo() {}
}
static MethodInfo
f_isA = ofm(F.class, "isA"),
f_is = ofm(F.class, "is"),
f_getA = ofm(F.class, "getA"),
f_get = ofm(F.class, "get"),
f_setA = ofm(F.class, "setA"),
f_set = ofm(F.class, "set"),
f_foo = ofm(F.class, "foo");
@Test
public void getPropertyName() {
assertEquals("a", f_isA.getPropertyName());
assertEquals("is", f_is.getPropertyName());
assertEquals("a", f_getA.getPropertyName());
assertEquals("get", f_get.getPropertyName());
assertEquals("a", f_setA.getPropertyName());
assertEquals("set", f_set.getPropertyName());
assertEquals("foo", f_foo.getPropertyName());
}
@Test
public void isBridge() {
assertFalse(f_foo.isBridge());
}
public static class G {
public void a1() {}
public void a1(int a1) {}
public void a1(int a1, int a2) {}
public void a1(String a1) {}
public void a2() {}
public void a3() {}
}
static MethodInfo
g_a1a = ofm(G.class, "a1"),
g_a1b = ofm(G.class, "a1", int.class),
g_a1c = ofm(G.class, "a1", int.class, int.class),
g_a1d = ofm(G.class, "a1", String.class),
g_a2 = ofm(G.class, "a2"),
g_a3 = ofm(G.class, "a3");
@Test
public void compareTo() {
Set<MethodInfo> s = new TreeSet<>(Arrays.asList(g_a1a, g_a1b, g_a1c, g_a1d, g_a2, g_a3));
check("[a1(), a1(int), a1(String), a1(int,int), a2(), a3()]", s);
}
}