1717import java .util .List ;
1818import java .util .Map ;
1919import java .util .Set ;
20+ import java .util .regex .Pattern ;
21+
22+ import static java .util .regex .Pattern .compile ;
2023
2124/**
2225 * A {@link Field} shorthand utility class used to collect fields from classes meeting Java Bean restrictions/requirements.
3639 */
3740@ UtilityClass
3841public final class BeanUtils {
39-
42+
4043 /**
4144 * Determines what visibility modifiers a field is allowed to have in {@link BeanUtils#collectFields(Class, Class, EnumSet, EnumSet)}.
4245 */
@@ -89,7 +92,11 @@ public enum BeanRestriction {
8992 }
9093
9194 /**
92- * Verifies is a given method occurs as setter or getter in the declaring class chain.
95+ * Verifies is a given method occurs as setter or getter in the declaring class chain. Lookup works by finding actual properties with
96+ * their respective getters/setters that follow bean convention.
97+ * <p>
98+ * Note that this is a strict lookup and interface methods are not considered bean methods. To include interfaces and their methods,
99+ * use {@link #isBeanMethod(Method, Class, EnumSet, boolean)} with <em>checkBeanLikeForInterfaces</em> set to {@code true}.
93100 * <p>
94101 * Lookup can be configured to check only against specific visibility.
95102 *
@@ -103,6 +110,20 @@ public enum BeanRestriction {
103110 @ SuppressWarnings ({"unused" , "WeakerAccess" })
104111 public static boolean isBeanMethod (final Method method , final Class <?> boundaryMarker ,
105112 final EnumSet <Visibility > visibility ) {
113+ return isBeanMethod (method , boundaryMarker , visibility , false );
114+ }
115+
116+ /**
117+ * @return Same as {@link #isBeanMethod(Method, Class, EnumSet)}, but may consider methods declared on interfaces as well.
118+ */
119+ private static boolean isBeanMethod (Method method , Class <?> boundaryMarker ,
120+ EnumSet <Visibility > visibility , boolean checkBeanLikeForInterfaces ) {
121+ return method .getDeclaringClass ().isInterface ()
122+ ? checkBeanLikeForInterfaces && methodIsBeanlike (method )
123+ : isBeanMethodForField (method , boundaryMarker , visibility );
124+ }
125+
126+ private static boolean isBeanMethodForField (Method method , Class <?> boundaryMarker , EnumSet <Visibility > visibility ) {
106127 Map <Class <?>, List <FieldWrapper >> fields = collectFields (method .getDeclaringClass (), boundaryMarker , visibility ,
107128 EnumSet .noneOf (BeanRestriction .class ));
108129 for (List <FieldWrapper > fieldWrappers : fields .values ()) {
@@ -114,7 +135,29 @@ public static boolean isBeanMethod(final Method method, final Class<?> boundaryM
114135 }
115136 return false ;
116137 }
117-
138+
139+ /**
140+ * Determines if the method <em>could</em> be a bean method by looking just at its name, parameters and presence of return type.
141+ *
142+ * @return True, is the method starts with set/get/is, has exactly one parameter and in case of
143+ * a primitive boolean the method should start with "isAbc"
144+ */
145+ @ SuppressWarnings ("WeakerAccess" )
146+ public static boolean methodIsBeanlike (Method method ) {
147+ final Pattern SET_PATTERN = compile ("set[A-Z].*?" );
148+ final Pattern GET_PATTERN = compile ("get[A-Z].*?" );
149+ final Pattern IS_PATTERN = compile ("is[A-Z].*?" );
150+
151+ final String name = method .getName ();
152+ final int paramCount = method .getParameterTypes ().length ;
153+ final Class <?> rt = method .getReturnType ();
154+
155+ return
156+ (rt == boolean .class && IS_PATTERN .matcher (name ).matches () && paramCount == 0 ) ||
157+ (rt != void .class && rt != boolean .class && GET_PATTERN .matcher (name ).matches () && paramCount == 0 ) ||
158+ (rt == void .class && SET_PATTERN .matcher (name ).matches () && paramCount == 1 );
159+ }
160+
118161 /**
119162 * Returns a pool of {@link Field} wrappers including optional relevant setter/getter methods, collected from the given class tested
120163 * against the given visibility and Bean restriction requirements.
0 commit comments