View Javadoc
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  					// ignore
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 }