1 package org.oxerr.commons.ws.rs.bean;
2
3 import java.beans.PropertyDescriptor;
4 import java.lang.annotation.Annotation;
5 import java.lang.reflect.Field;
6 import java.lang.reflect.InvocationTargetException;
7 import java.util.Arrays;
8 import java.util.HashSet;
9 import java.util.List;
10 import java.util.Set;
11 import java.util.stream.Collectors;
12
13 import org.springframework.beans.BeanWrapper;
14 import org.springframework.beans.BeanWrapperImpl;
15 import org.springframework.beans.InvalidPropertyException;
16
17 public final class BeanUtils {
18
19 private BeanUtils() {
20 }
21
22 public static <T> T patch(T dest, Object orig) {
23 if (dest == null || orig == null) {
24 return dest;
25 }
26
27 try {
28 NullAwareBeanUtilsBean.getInstance().copyProperties(dest, orig);
29 } catch (IllegalAccessException | InvocationTargetException e) {
30 throw new IllegalArgumentException(e);
31 }
32
33 return dest;
34 }
35
36 public static <T> T patch(T dest, Object orig, String... properties) {
37 if (dest == null || orig == null) {
38 return dest;
39 }
40
41 final BeanWrapper destBeanWrapper = new BeanWrapperImpl(dest);
42 final BeanWrapper origBeanWrapper = new BeanWrapperImpl(orig);
43
44 for (final String property : properties) {
45 final Object value = origBeanWrapper.getPropertyValue(property);
46 if (value != null) {
47 destBeanWrapper.setPropertyValue(property, value);
48 }
49 }
50
51 return dest;
52 }
53
54 public static <T> T patchExclude(T dest, Object orig,
55 String... excludedProperties) {
56 if (dest == null || orig == null) {
57 return dest;
58 }
59
60 final List<String> excludedPropertyList = Arrays.asList(excludedProperties);
61
62 final BeanWrapper destBeanWrapper = new BeanWrapperImpl(dest);
63 final BeanWrapper origBeanWrapper = new BeanWrapperImpl(orig);
64
65 for (final PropertyDescriptor pd : destBeanWrapper.getPropertyDescriptors()) {
66 if (pd.getWriteMethod() != null && !excludedPropertyList.contains(pd.getName())) {
67 final Object value = origBeanWrapper.getPropertyValue(pd.getName());
68 if (value != null) {
69 destBeanWrapper.setPropertyValue(pd.getName(), value);
70 }
71 }
72 }
73
74 return dest;
75 }
76
77 public static <T> T patchExclude(T dest, Object orig,
78 Set<Class<? extends Annotation>> excludedAnnotationTypes) {
79 if (dest == null || orig == null) {
80 return dest;
81 }
82
83 final BeanWrapper destBeanWrapper = new BeanWrapperImpl(dest);
84 final BeanWrapper origBeanWrapper = new BeanWrapperImpl(orig);
85
86 for (final PropertyDescriptor pd : destBeanWrapper.getPropertyDescriptors()) {
87 if (pd.getWriteMethod() != null) {
88 final Set<Annotation> annotations = new HashSet<>();
89 annotations.addAll(Arrays.asList(pd.getReadMethod().getAnnotations()));
90 annotations.addAll(Arrays.asList(pd.getWriteMethod().getAnnotations()));
91 try {
92 Field field = dest.getClass().getDeclaredField(pd.getName());
93 annotations.addAll(Arrays.asList(field.getAnnotations()));
94 } catch (NoSuchFieldException | SecurityException e) {
95
96 }
97
98 final List<Class<? extends Annotation>> annotationTypes = annotations
99 .stream().map(Annotation::annotationType)
100 .collect(Collectors.toList());
101 annotationTypes.retainAll(excludedAnnotationTypes);
102
103 if (annotationTypes.isEmpty()) {
104 setPropertyValue(destBeanWrapper, origBeanWrapper, pd);
105 }
106 }
107 }
108
109 return dest;
110 }
111
112 private static void setPropertyValue(
113 final BeanWrapper destBeanWrapper,
114 final BeanWrapper origBeanWrapper,
115 final PropertyDescriptor pd
116 ) {
117 try {
118 final Object value = origBeanWrapper.getPropertyValue(pd.getName());
119 if (value != null) {
120 destBeanWrapper.setPropertyValue(pd.getName(), value);
121 }
122 } catch (InvalidPropertyException e) {
123 if (!isCausedByIgnorableException(e)) {
124 throw e;
125 }
126 }
127 }
128
129 private static boolean isCausedByIgnorableException(InvalidPropertyException e) {
130 if (!(e.getCause() instanceof InvocationTargetException)) {
131 return false;
132 }
133
134 final Throwable cause = e.getCause().getCause();
135
136 if (cause == null) {
137 return false;
138 } else if (cause instanceof NoSuchFieldException) {
139 return true;
140 } else if (cause instanceof SecurityException) {
141 return true;
142 }
143
144 return false;
145 }
146
147 @SuppressWarnings("unchecked")
148 public static <T> T patchExclude(T dest, Object orig,
149 Class<?>... excludedAnnotationTypes) {
150 patchExclude(dest, orig,
151 Arrays.asList(excludedAnnotationTypes).stream()
152 .map(e -> (Class<? extends Annotation>) e)
153 .collect(Collectors.toSet()));
154 return dest;
155 }
156
157 public static <T> T copyProperties(T dest, Object orig, String... properties) {
158 if (dest == null || orig == null) {
159 return dest;
160 }
161
162 final BeanWrapper destBeanWrapper = new BeanWrapperImpl(dest);
163 final BeanWrapper origBeanWrapper = new BeanWrapperImpl(orig);
164 for (final String property : properties) {
165 final Object value = origBeanWrapper.getPropertyValue(property);
166 destBeanWrapper.setPropertyValue(property, value);
167 }
168 return dest;
169 }
170
171 }