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