@@ -66,8 +66,8 @@ static <T> PojoType<T> of(Class<T> pojoClass) {
6666 Constructor <T > ctor = requirePublicDefaultConstructor (pojoClass );
6767
6868 Map <String , Field > allFields = discoverAllInstanceFields (pojoClass );
69- Map <String , Method > getters = discoverGetters (pojoClass );
70- Map <String , Method > setters = discoverSetters (pojoClass );
69+ Map <String , Method > getters = discoverGetters (pojoClass , allFields );
70+ Map <String , Method > setters = discoverSetters (pojoClass , allFields );
7171
7272 Map <String , Property > props = new LinkedHashMap <>();
7373 for (Map .Entry <String , Field > e : allFields .entrySet ()) {
@@ -85,10 +85,11 @@ static <T> PojoType<T> of(Class<T> pojoClass) {
8585 if (!publicField ) {
8686 // When not a public field, require both getter and setter
8787 if (getter == null || setter == null ) {
88+ final String capitalizedName = capitalize (name );
8889 throw new IllegalArgumentException (
8990 String .format (
9091 "POJO class %s field '%s' must be public or have both getter and setter (get%s/set%s)." ,
91- pojoClass .getName (), name , capitalize ( name ), capitalize ( name ) ));
92+ pojoClass .getName (), name , capitalizedName , capitalizedName ));
9293 }
9394 }
9495 props .put (
@@ -108,22 +109,30 @@ private static <T> void validatePublicClass(Class<T> pojoClass) {
108109 }
109110
110111 private static <T > Constructor <T > requirePublicDefaultConstructor (Class <T > pojoClass ) {
111- try {
112- Constructor <T > ctor = pojoClass .getDeclaredConstructor ();
113- if (!Modifier .isPublic (ctor .getModifiers ())) {
114- throw new IllegalArgumentException (
115- String .format (
116- "POJO class %s must have a public default constructor." ,
117- pojoClass .getName ()));
112+ Constructor <T >[] ctors = (Constructor <T >[]) pojoClass .getConstructors ();
113+ for (Constructor <T > c : ctors ) {
114+ if (!Modifier .isPublic (c .getModifiers ())) {
115+ continue ;
116+ }
117+ if (c .getParameterCount () == 0 ) {
118+
119+ return c ;
120+ }
121+ if (c .getParameterCount () == 1
122+ && pojoClass
123+ .getName ()
124+ .equals (
125+ c .getParameterTypes ()[0 ].getName ()
126+ + "$"
127+ + pojoClass .getSimpleName ())) {
128+ return c ;
118129 }
119- return ctor ;
120- } catch (NoSuchMethodException e ) {
121- throw new IllegalArgumentException (
122- String .format (
123- "POJO class %s must have a public default constructor." ,
124- pojoClass .getName ()),
125- e );
126130 }
131+
132+ throw new IllegalArgumentException (
133+ String .format (
134+ "POJO class %s must have a public default constructor." ,
135+ pojoClass .getName ()));
127136 }
128137
129138 private static Map <String , Field > discoverAllInstanceFields (Class <?> clazz ) {
@@ -135,6 +144,13 @@ private static Map<String, Field> discoverAllInstanceFields(Class<?> clazz) {
135144 if (Modifier .isStatic (mod ) || Modifier .isTransient (mod )) {
136145 continue ;
137146 }
147+ // Skip references to enclosing class
148+ if (f .getName ().startsWith ("this$" )) {
149+ final Class type = f .getType ();
150+ if ((type .getName () + "$" + clazz .getSimpleName ()).equals (clazz .getName ())) {
151+ continue ;
152+ }
153+ }
138154 f .setAccessible (true );
139155 fields .putIfAbsent (f .getName (), f );
140156 }
@@ -143,32 +159,68 @@ private static Map<String, Field> discoverAllInstanceFields(Class<?> clazz) {
143159 return fields ;
144160 }
145161
146- private static Map <String , Method > discoverGetters (Class <?> clazz ) {
147- Map <String , Method > getters = new HashMap <>();
162+ private static Map <String , Method > discoverGetters (
163+ Class <?> clazz , Map <String , Field > fieldMap ) {
164+ final Map <String , Method > getters = new HashMap <>();
148165 for (Method m : clazz .getMethods ()) { // public methods incl. inherited
149- if (m .getParameterCount () == 0
150- && m .getName ().startsWith ("get" )
151- && !m .getReturnType ().equals (void .class )) {
152- String prop = decapitalize (m .getName ().substring (3 ));
166+ final String prop = getGetterProp (m );
167+ if (fieldMap .containsKey (prop )) {
153168 getters .put (prop , m );
154169 }
155170 }
156171 return getters ;
157172 }
158173
159- private static Map <String , Method > discoverSetters (Class <?> clazz ) {
160- Map <String , Method > setters = new HashMap <>();
174+ private static String getGetterProp (Method m ) {
175+ if (m .getParameterCount () != 0 ) {
176+ return null ;
177+ }
178+ final Class <?> returnType = m .getReturnType ();
179+ if (void .class .equals (returnType ) || Void .class .equals (returnType )) {
180+ return null ;
181+ }
182+ final String name = m .getName ();
183+ if (name .startsWith ("get" )) {
184+ return decapitalize (name .substring (3 ));
185+ }
186+ if (returnType .equals (boolean .class ) || returnType .equals (Boolean .class )) {
187+ if (name .startsWith ("is" )) {
188+ return decapitalize (name .substring (2 ));
189+ }
190+ if (name .startsWith ("has" )) {
191+ return decapitalize (name .substring (3 ));
192+ }
193+ }
194+ return null ;
195+ }
196+
197+ private static Map <String , Method > discoverSetters (
198+ Class <?> clazz , Map <String , Field > fieldMap ) {
199+ final Map <String , Method > setters = new HashMap <>();
161200 for (Method m : clazz .getMethods ()) { // public methods incl. inherited
162- if (m .getParameterCount () == 1
163- && m .getName ().startsWith ("set" )
164- && m .getReturnType ().equals (void .class )) {
165- String prop = decapitalize (m .getName ().substring (3 ));
201+ final String prop = getSetterProp (m );
202+ if (fieldMap .containsKey (prop )) {
166203 setters .put (prop , m );
167204 }
168205 }
169206 return setters ;
170207 }
171208
209+ private static String getSetterProp (Method m ) {
210+ if (m .getParameterCount () != 1 ) {
211+ return null ;
212+ }
213+ final Class <?> returnType = m .getReturnType ();
214+ if (!void .class .equals (returnType ) && !Void .class .equals (returnType )) {
215+ return null ;
216+ }
217+ final String name = m .getName ();
218+ if (name .startsWith ("set" )) {
219+ return decapitalize (name .substring (3 ));
220+ }
221+ return null ;
222+ }
223+
172224 private static String capitalize (String s ) {
173225 if (s == null || s .isEmpty ()) {
174226 return s ;
0 commit comments