1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package com.codebox.bean;
16
17 import java.lang.reflect.Method;
18 import java.util.HashMap;
19 import java.util.Map;
20
21
22
23
24 public final class ByteBuddyBeanCopier {
25
26
27
28
29 private ByteBuddyBeanCopier() {
30 throw new AssertionError("Utility class should not be instantiated");
31 }
32
33
34
35
36
37
38
39
40
41
42
43 public static void copy(Object source, Object target, BooleanConverter converter) {
44 if (source == null || target == null) {
45 return;
46 }
47
48 Class<?> clazz = source.getClass();
49 Map<String, Method> getters = new HashMap<>();
50 Map<String, Method> setters = new HashMap<>();
51
52 for (Method method : clazz.getMethods()) {
53 if (ByteBuddyBeanCopier.isGetter(method)) {
54 String property = ByteBuddyBeanCopier.decapitalize(ByteBuddyBeanCopier.extractPropertyName(method));
55 getters.put(property, method);
56 } else if (ByteBuddyBeanCopier.isSetter(method)) {
57 String property = ByteBuddyBeanCopier.decapitalize(method.getName().substring(3));
58 setters.put(property, method);
59 }
60 }
61
62 for (Map.Entry<String, Method> entry : setters.entrySet()) {
63 String property = entry.getKey();
64 Method setter = entry.getValue();
65 Method getter = getters.get(property);
66
67 try {
68 Object value = null;
69 if (getter != null) {
70 value = getter.invoke(source);
71 } else if (isBooleanType(setter.getParameterTypes()[0])) {
72
73 String isMethodName = "is" + ByteBuddyBeanCopier.capitalize(property);
74 try {
75 Method isMethod = clazz.getMethod(isMethodName);
76 if (isBooleanType(isMethod.getReturnType())) {
77 value = isMethod.invoke(source);
78 }
79 } catch (NoSuchMethodException ignored) {
80
81 }
82 }
83 if (converter != null && isBooleanType(setter.getParameterTypes()[0])) {
84 value = converter.convert(value, setter.getParameterTypes()[0]);
85 }
86 if (value != null || !setter.getParameterTypes()[0].isPrimitive()) {
87 setter.invoke(target, value);
88 }
89 } catch (Exception e) {
90 throw new RuntimeException("Failed to copy property: " + property, e);
91 }
92 }
93 }
94
95
96
97
98
99
100
101
102 private static boolean isGetter(Method method) {
103 if (method.getParameterCount() != 0) {
104 return false;
105 }
106 String name = method.getName();
107 return name.startsWith("get") && !method.getReturnType().equals(void.class) && name.length() > 3
108 || name.startsWith("is") && isBooleanType(method.getReturnType()) && name.length() > 2;
109 }
110
111
112
113
114
115
116
117
118 private static boolean isSetter(Method method) {
119 return method.getName().startsWith("set") && method.getParameterCount() == 1
120 && method.getReturnType().equals(void.class);
121 }
122
123
124
125
126
127
128
129
130 private static boolean isBooleanType(Class<?> type) {
131 return type == boolean.class || type == Boolean.class;
132 }
133
134
135
136
137
138
139
140
141 private static String extractPropertyName(Method getter) {
142 String name = getter.getName();
143 if (name.startsWith("get")) {
144 return name.substring(3);
145 }
146 if (name.startsWith("is")) {
147 return name.substring(2);
148 }
149 throw new IllegalArgumentException("Not a getter method: " + name);
150 }
151
152
153
154
155
156
157
158
159 private static String decapitalize(String name) {
160 if (name == null || name.isEmpty()) {
161 return name;
162 }
163 if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && Character.isUpperCase(name.charAt(0))) {
164 return name;
165 }
166 return Character.toLowerCase(name.charAt(0)) + name.substring(1);
167 }
168
169
170
171
172
173
174
175
176 private static String capitalize(String name) {
177 if (name == null || name.isEmpty()) {
178 return name;
179 }
180 return Character.toUpperCase(name.charAt(0)) + name.substring(1);
181 }
182
183
184
185
186 public interface BooleanConverter {
187
188
189
190
191
192
193
194
195
196
197 Object convert(Object value, Class<?> targetType);
198 }
199
200 }