This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new 62d65b262a Add findGetter/Setter methods to ClassInfo
62d65b262a is described below
commit 62d65b262af96dbc6d1e7f0a6939d63abdbdfee5
Author: James Bognar <[email protected]>
AuthorDate: Thu Mar 19 09:49:48 2026 -0400
Add findGetter/Setter methods to ClassInfo
---
.../apache/juneau/commons/reflect/ClassInfo.java | 41 ++++++++++++++++++
.../apache/juneau/commons/reflect/MethodInfo.java | 35 ++++++++++++++++
.../juneau/commons/reflect/ClassInfo_Test.java | 40 ++++++++++++++++++
.../juneau/commons/reflect/MethodInfo_Test.java | 48 ++++++++++++++++++++++
todo/TODO.md | 1 -
5 files changed, 164 insertions(+), 1 deletion(-)
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ClassInfo.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ClassInfo.java
index fb577325da..8c8fa2ef7a 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ClassInfo.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ClassInfo.java
@@ -1667,6 +1667,47 @@ public class ClassInfo extends ElementInfo implements
Annotatable, Type, Compara
return publicMethods.get().stream().filter(x -> test(filter,
x)).findFirst();
}
+ /**
+ * Finds a public getter method for the specified bean property name.
+ *
+ * <p>
+ * Matches any public no-argument method named {@code
get<PropertyName>} with a non-void return type,
+ * or {@code is<PropertyName>} with a {@code boolean}/{@link Boolean}
return type.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// Find the getter for the "name" property on MyBean</jc>
+ * Optional<MethodInfo> <jv>getter</jv> =
ClassInfo.<jsm>info</jsm>(MyBean.<jk>class</jk>).findGetter(<js>"name"</js>);
+ * <jv>getter</jv>.ifPresent(<jv>m</jv> ->
System.<jsf>out</jsf>.println(<jv>m</jv>.getName())); <jc>// "getName"</jc>
+ * </p>
+ *
+ * @param propertyName The bean property name (e.g. <js>"name"</js> to
match {@code getName()} or {@code isName()}).
+ * @return The matching getter method, or an empty {@link Optional} if
not found.
+ */
+ public Optional<MethodInfo> findGetter(String propertyName) {
+ return getPublicMethod(m -> m.isGetter() &&
m.getPropertyName().equals(propertyName));
+ }
+
+ /**
+ * Finds a public setter method for the specified bean property name.
+ *
+ * <p>
+ * Matches any public single-argument method named {@code
set<PropertyName>}.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// Find the setter for the "name" property on MyBean</jc>
+ * Optional<MethodInfo> <jv>setter</jv> =
ClassInfo.<jsm>info</jsm>(MyBean.<jk>class</jk>).findSetter(<js>"name"</js>);
+ * <jv>setter</jv>.ifPresent(<jv>m</jv> ->
System.<jsf>out</jsf>.println(<jv>m</jv>.getName())); <jc>// "setName"</jc>
+ * </p>
+ *
+ * @param propertyName The bean property name (e.g. <js>"name"</js> to
match {@code setName(X)}).
+ * @return The matching setter method, or an empty {@link Optional} if
not found.
+ */
+ public Optional<MethodInfo> findSetter(String propertyName) {
+ return getPublicMethod(m -> m.isSetter() &&
m.getPropertyName().equals(propertyName));
+ }
+
/**
* Returns all public methods on this class and parent classes.
*
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/MethodInfo.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/MethodInfo.java
index ab21d7c6af..9de17c9e69 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/MethodInfo.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/MethodInfo.java
@@ -405,6 +405,41 @@ public class MethodInfo extends ExecutableInfo implements
Comparable<MethodInfo>
return n;
}
+ /**
+ * Returns <jk>true</jk> if this method follows the Java Bean getter
convention.
+ *
+ * <p>
+ * A getter is a public no-argument method whose name starts with
{@code get} (with a non-void return type)
+ * or {@code is} (with a {@code boolean}/{@link Boolean} return type).
+ *
+ * @return <jk>true</jk> if this method is a bean getter.
+ */
+ public boolean isGetter() {
+ var n = inner.getName();
+ if (getParameterCount() != 0)
+ return false;
+ if (n.startsWith("get") && n.length() > 3)
+ return !getReturnType().is(Void.TYPE);
+ if (n.startsWith("is") && n.length() > 2) {
+ var rt = getReturnType();
+ return rt.is(Boolean.TYPE) || rt.is(Boolean.class);
+ }
+ return false;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this method follows the Java Bean setter
convention.
+ *
+ * <p>
+ * A setter is a method whose name starts with {@code set} and accepts
exactly one argument.
+ *
+ * @return <jk>true</jk> if this method is a bean setter.
+ */
+ public boolean isSetter() {
+ var n = inner.getName();
+ return n.startsWith("set") && n.length() > 3 &&
getParameterCount() == 1;
+ }
+
/**
* Returns the generic return type of this method as a {@link
ClassInfo} object.
*
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/ClassInfo_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/ClassInfo_Test.java
index c157656666..22cfdc91e9 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/ClassInfo_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/ClassInfo_Test.java
@@ -4273,5 +4273,45 @@ public class ClassInfo_Test extends TestBase {
assertSame(service, bean.injectedService, "PostConstruct should
see injected field");
assertTrue(bean.postConstructCalled, "PostConstruct should be
called after all injection");
}
+
+
//====================================================================================================
+ // findGetter() / findSetter()
+
//====================================================================================================
+
+ public static class B038_FindGetterSetterBean {
+ private String name;
+ private boolean active;
+
+ public String getName() { return name; }
+ public void setName(String value) { this.name = value; }
+ public boolean isActive() { return active; }
+ }
+
+ @Test
+ void b038_findGetter() {
+ var ci = ClassInfo.of(B038_FindGetterSetterBean.class);
+
+ var getter = ci.findGetter("name");
+ assertTrue(getter.isPresent());
+ assertEquals("getName", getter.get().getName());
+
+ var isGetter = ci.findGetter("active");
+ assertTrue(isGetter.isPresent());
+ assertEquals("isActive", isGetter.get().getName());
+
+ assertFalse(ci.findGetter("nonExistent").isPresent());
+ }
+
+ @Test
+ void b039_findSetter() {
+ var ci = ClassInfo.of(B038_FindGetterSetterBean.class);
+
+ var setter = ci.findSetter("name");
+ assertTrue(setter.isPresent());
+ assertEquals("setName", setter.get().getName());
+
+ assertFalse(ci.findSetter("active").isPresent()); // no
setActive method
+ assertFalse(ci.findSetter("nonExistent").isPresent());
+ }
}
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/MethodInfo_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/MethodInfo_Test.java
index fe36a9fa2a..63f9d26f0d 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/MethodInfo_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/MethodInfo_Test.java
@@ -1015,5 +1015,53 @@ class MethodInfo_Test extends TestBase {
var method =
ClassInfo.of(TestMethodClass.class).getPublicMethod(x -> x.hasName("method1")
&& x.hasParameterTypes(TestService.class)).get();
assertThrows(ExecutableException.class, () ->
method.inject(beanStore, instance));
}
+
+
//====================================================================================================
+ // isGetter()
+
//====================================================================================================
+
+ public static class A042_IsGetterBean {
+ public String getString() { return null; }
+ public boolean isActive() { return false; }
+ public Boolean isBoxedBoolean() { return false; }
+ public void getVoid() {}
+ public String get() { return null; }
+ public boolean is() { return false; }
+ public void foo() {}
+ }
+
+ @Test
+ void a042_isGetter() throws NoSuchMethodException {
+ var c = A042_IsGetterBean.class;
+ assertTrue(MethodInfo.of(c.getMethod("getString")).isGetter());
// get + non-void return
+ assertTrue(MethodInfo.of(c.getMethod("isActive")).isGetter());
// is + boolean return
+
assertTrue(MethodInfo.of(c.getMethod("isBoxedBoolean")).isGetter()); // is +
Boolean return
+ assertFalse(MethodInfo.of(c.getMethod("getVoid")).isGetter());
// get + void return
+ assertFalse(MethodInfo.of(c.getMethod("get")).isGetter());
// "get" alone — too short
+ assertFalse(MethodInfo.of(c.getMethod("is")).isGetter());
// "is" alone — too short
+ assertFalse(MethodInfo.of(c.getMethod("foo")).isGetter());
// no getter prefix
+ }
+
+
//====================================================================================================
+ // isSetter()
+
//====================================================================================================
+
+ public static class A043_IsSetterBean {
+ public void setName(String value) {}
+ public void set() {}
+ public void setMulti(String a, String b) {}
+ public String getName() { return null; }
+ public void foo(String value) {}
+ }
+
+ @Test
+ void a043_isSetter() throws NoSuchMethodException {
+ var c = A043_IsSetterBean.class;
+ assertTrue(MethodInfo.of(c.getMethod("setName",
String.class)).isSetter()); // set + one param
+ assertFalse(MethodInfo.of(c.getMethod("set")).isSetter());
// "set" alone — too short
+ assertFalse(MethodInfo.of(c.getMethod("setMulti", String.class,
String.class)).isSetter()); // set + two params
+ assertFalse(MethodInfo.of(c.getMethod("getName")).isSetter());
// wrong prefix
+ assertFalse(MethodInfo.of(c.getMethod("foo",
String.class)).isSetter()); // no setter prefix
+ }
}
diff --git a/todo/TODO.md b/todo/TODO.md
index 6d936744e0..9e8b2bff04 100644
--- a/todo/TODO.md
+++ b/todo/TODO.md
@@ -3,7 +3,6 @@
- Investigate navlinks URL generation issue: Either
"request:?Accept=text/json&plainText=true" should be supported, or
"request:/?Accept=text/json&plainText=true" should not append '/' to the
request URL. Currently, "request:/?Accept=..." generates URLs like
"http://localhost:5000/rest/db/request:?Accept=..." which is incorrect.
- Update REST server API to use new BeanStore2.
-- ClassInfo should have a findGetter(String propertyName) convenience method.
- Make sure @Beanp("*") works on plain fields.
- Need an easier way to specify this header:
Content-Disposition: attachment; filename="example.pdf"