17
17
import java .util .List ;
18
18
import java .util .Map ;
19
19
import java .util .Set ;
20
+ import java .util .regex .Pattern ;
21
+
22
+ import static java .util .regex .Pattern .compile ;
20
23
21
24
/**
22
25
* A {@link Field} shorthand utility class used to collect fields from classes meeting Java Bean restrictions/requirements.
36
39
*/
37
40
@ UtilityClass
38
41
public final class BeanUtils {
39
-
42
+
40
43
/**
41
44
* Determines what visibility modifiers a field is allowed to have in {@link BeanUtils#collectFields(Class, Class, EnumSet, EnumSet)}.
42
45
*/
@@ -89,7 +92,11 @@ public enum BeanRestriction {
89
92
}
90
93
91
94
/**
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}.
93
100
* <p>
94
101
* Lookup can be configured to check only against specific visibility.
95
102
*
@@ -103,6 +110,20 @@ public enum BeanRestriction {
103
110
@ SuppressWarnings ({"unused" , "WeakerAccess" })
104
111
public static boolean isBeanMethod (final Method method , final Class <?> boundaryMarker ,
105
112
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 ) {
106
127
Map <Class <?>, List <FieldWrapper >> fields = collectFields (method .getDeclaringClass (), boundaryMarker , visibility ,
107
128
EnumSet .noneOf (BeanRestriction .class ));
108
129
for (List <FieldWrapper > fieldWrappers : fields .values ()) {
@@ -114,7 +135,29 @@ public static boolean isBeanMethod(final Method method, final Class<?> boundaryM
114
135
}
115
136
return false ;
116
137
}
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
+
118
161
/**
119
162
* Returns a pool of {@link Field} wrappers including optional relevant setter/getter methods, collected from the given class tested
120
163
* against the given visibility and Bean restriction requirements.
0 commit comments