View Javadoc
1   package net.avcompris.commons3.web.it.utils;
2   
3   import static com.google.common.base.Preconditions.checkArgument;
4   import static com.google.common.base.Preconditions.checkNotNull;
5   import static com.google.common.collect.Sets.newHashSet;
6   import static org.apache.commons.lang3.StringUtils.uncapitalize;
7   
8   import java.io.IOException;
9   import java.lang.reflect.Array;
10  import java.lang.reflect.InvocationHandler;
11  import java.lang.reflect.Method;
12  import java.lang.reflect.Proxy;
13  import java.util.Set;
14  
15  import org.apache.commons.lang3.NotImplementedException;
16  import org.json.simple.JSONArray;
17  import org.json.simple.JSONObject;
18  import org.json.simple.parser.JSONParser;
19  import org.json.simple.parser.ParseException;
20  
21  import net.avcompris.commons3.core.DateTimeHolderImpl;
22  import net.avcompris.commons3.types.DateTimeHolder;
23  
24  public abstract class JSONUtils {
25  
26  	public static <T> T parseJSON(final String json, final Class<T> clazz) throws IOException, ParseException {
27  
28  		checkNotNull(json, "json");
29  		checkNotNull(clazz, "clazz");
30  
31  		// final ObjectMapper mapper = new ObjectMapper();
32  		//
33  		// final SimpleModule module = new SimpleModule();
34  		//
35  		// addDeserializer(module, clazz);
36  		//
37  		// mapper.registerModule(module);
38  		//
39  		// return mapper.readValue(json, clazz);
40  
41  		final JSONParser parser = new JSONParser();
42  
43  		final JSONObject jsonObject = (JSONObject) parser.parse(json);
44  
45  		return parseJSON(jsonObject, clazz);
46  	}
47  
48  	public static <T> T parseJSON(final JSONObject json, final Class<T> clazz) throws IOException {
49  
50  		checkNotNull(json, "json");
51  		checkNotNull(clazz, "clazz");
52  
53  		checkArgument(clazz.isInterface(), "clazz should be an interface: %s", clazz.getName());
54  
55  		final ClassLoader classLoader = // Thread.currentThread().getContextClassLoader();
56  				clazz.getClassLoader();
57  
58  		final Object proxy = Proxy.newProxyInstance(classLoader, new Class<?>[] { //
59  				clazz //
60  		}, new InvocationHandler() {
61  
62  			@Override
63  			public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
64  
65  				final int parameterCount = method.getParameterCount();
66  
67  				final String methodName = method.getName();
68  
69  				if (parameterCount == 0) {
70  
71  					if ("getClass".contentEquals(methodName)) {
72  
73  						throw new NotImplementedException("method: " + method);
74  
75  					} else if ("hashCode".contentEquals(methodName)) {
76  
77  						return json.hashCode();
78  
79  					} else if ("toString".contentEquals(methodName)) {
80  
81  						return json.toJSONString();
82  					}
83  
84  					final String suffix;
85  
86  					if (methodName.startsWith("get")) {
87  
88  						suffix = methodName.substring(3);
89  
90  					} else if (methodName.startsWith("is")) {
91  
92  						suffix = methodName.substring(2);
93  
94  					} else {
95  
96  						throw new NotImplementedException("method: " + method);
97  					}
98  
99  					final String propertyName = uncapitalize(suffix);
100 
101 					final Object value = json.get(propertyName);
102 
103 					final Class<?> returnType = method.getReturnType();
104 
105 					if (returnType == null) {
106 						throw new NotImplementedException("method: " + method);
107 					}
108 
109 					if (boolean.class.equals(returnType)) {
110 
111 						if (value instanceof Boolean) {
112 
113 							return (boolean) value;
114 
115 						} else {
116 
117 							throw new NotImplementedException("property \"" + propertyName + "\" should be a boolean: "
118 									+ value + ", but was: " + value.getClass().getName());
119 						}
120 
121 					} else if (int.class.equals(returnType)) {
122 
123 						if (value instanceof Long) {
124 
125 							return ((Long) value).intValue();
126 
127 						} else if (value instanceof Integer) {
128 
129 							return (int) value;
130 
131 						} else {
132 
133 							throw new NotImplementedException(
134 									"property \"" + propertyName + "\" should be a long or an int: " + value
135 											+ ", but was: " + value.getClass().getName());
136 						}
137 
138 					} else if (String.class.equals(returnType)) {
139 
140 						if (value == null) {
141 
142 							return null;
143 
144 						} else if (value instanceof String) {
145 
146 							return (String) value;
147 
148 						} else {
149 
150 							throw new NotImplementedException("property \"" + propertyName + "\" should be a String: "
151 									+ value + ", but was: " + value.getClass().getName());
152 						}
153 
154 					} else if (DateTimeHolder.class.equals(returnType)) {
155 
156 						if (value == null) {
157 
158 							return null;
159 
160 						} else if (value instanceof JSONObject) {
161 
162 							@SuppressWarnings("unused")
163 							final String dateTime = (String) ((JSONObject) value).get("dateTime");
164 
165 							final long timestamp = (Long) ((JSONObject) value).get("timestamp");
166 
167 							return DateTimeHolderImpl.toDateTimeHolder(timestamp);
168 
169 						} else {
170 
171 							throw new NotImplementedException(
172 									"property \"" + propertyName + "\" should be a DateTimeHolder: " + value);
173 						}
174 
175 					} else if (returnType.isEnum()) {
176 
177 						if (value == null) {
178 
179 							return null;
180 
181 						} else if (value instanceof String) {
182 
183 							final String name = (String) value;
184 
185 							for (final Object constant : returnType.getEnumConstants()) {
186 
187 								final Enum<?> enumConstant = (Enum<?>) constant;
188 
189 								if (name.equals(enumConstant.name())) {
190 
191 									return enumConstant;
192 								}
193 							}
194 
195 							throw new IOException("Illegal Enum value for \"" + propertyName + "\": " + value
196 									+ " (Enum class: " + returnType.getName() + ")");
197 
198 						} else {
199 
200 							throw new NotImplementedException("property \"" + propertyName + "\" should be a String: "
201 									+ value + ", but was: " + value.getClass().getName());
202 						}
203 
204 					} else if (returnType.isInterface()) {
205 
206 						if (value == null) {
207 
208 							return null;
209 
210 						} else if (value instanceof JSONObject) {
211 
212 							return parseJSON((JSONObject) value, returnType);
213 
214 						} else {
215 
216 							throw new NotImplementedException("property \"" + propertyName
217 									+ "\" should be a JSONObject, but was: " + value.getClass().getName());
218 
219 						}
220 
221 					} else if (returnType.isArray()) {
222 
223 						if (value == null) {
224 
225 							return null;
226 
227 						} else if (value instanceof JSONArray) {
228 
229 							final JSONArray jsonArray = (JSONArray) value;
230 
231 							final int count = jsonArray.size();
232 
233 							final Class<?> componentType = returnType.getComponentType();
234 
235 							final Object array = Array.newInstance(componentType, count);
236 
237 							for (int i = 0; i < count; ++i) {
238 
239 								final Object item = parseJSON((JSONObject) jsonArray.get(i), componentType);
240 
241 								Array.set(array, i, item);
242 							}
243 
244 							return array;
245 
246 						} else {
247 
248 							throw new NotImplementedException("property \"" + propertyName
249 									+ "\" should be an array, but was: " + value.getClass().getName());
250 						}
251 					}
252 
253 				} else if (parameterCount == 1 //
254 						&& "equals".contentEquals(methodName) //
255 						&& Object.class.equals(method.getParameterTypes()[0])) {
256 
257 					final Object arg = args[0];
258 
259 					if (arg == null || !clazz.isInstance(arg)) {
260 						return false;
261 					}
262 
263 					final Set<Method> getters1 = extractGetters(clazz);
264 					final Set<Method> getters2 = extractGetters(arg.getClass());
265 
266 					if (getters1.size() != getters2.size()) {
267 						return false;
268 					}
269 
270 					for (final Method getter : getters1) {
271 
272 						final Object result1 = getter.invoke(proxy);
273 						final Object result2 = getter.invoke(arg);
274 
275 						if (result1 == null && result2 == null) {
276 							continue;
277 						} else if (result1 == null || result2 == null) {
278 							return false;
279 						}
280 
281 						final Class<?> returnType = getter.getReturnType();
282 
283 						if (returnType.isArray()) {
284 
285 							final int size1 = Array.getLength(result1);
286 							final int size2 = Array.getLength(result2);
287 
288 							if (size1 != size2) {
289 								return false;
290 							}
291 
292 							for (int i = 0; i < size1; ++i) {
293 
294 								final Object item1 = Array.get(result1, i);
295 								final Object item2 = Array.get(result2, i);
296 
297 								if (item1 == null && item2 == null) {
298 									continue;
299 								} else if (item1 == null || item2 == null) {
300 									return false;
301 								} else if (!item1.equals(item2)) {
302 									return false;
303 								}
304 							}
305 
306 						} else if (!result1.equals(result2)) {
307 							return false;
308 						}
309 					}
310 
311 					return true;
312 				}
313 
314 				throw new NotImplementedException("method: " + method);
315 			}
316 		});
317 
318 		return clazz.cast(proxy);
319 	}
320 
321 	private static Set<Method> extractGetters(final Class<?> clazz) {
322 
323 		final Set<Method> getters = newHashSet();
324 
325 		for (final Method method : clazz.getMethods()) {
326 
327 			final int parameterCount = method.getParameterCount();
328 
329 			final String methodName = method.getName();
330 
331 			if (parameterCount != 0 //
332 					|| "notify".contentEquals(methodName) //
333 					|| "notifyAll".contentEquals(methodName) //
334 					|| "wait".contentEquals(methodName) //
335 					|| "getClass".contentEquals(methodName) //
336 					|| "hashCode".contentEquals(methodName) //
337 					|| "toString".contentEquals(methodName)) {
338 
339 				continue;
340 			}
341 
342 			getters.add(method);
343 		}
344 
345 		return getters;
346 	}
347 
348 //	private static <T> DataBeanDeserializer<T> addDeserializer(final SimpleModule module, final Class<T> clazz) {
349 	//
350 	// final DataBeanDeserializer<T> deserializer =
351 	// DataBeanDeserializer.createDeserializer(clazz);
352 	//
353 	// module.addDeserializer(clazz, deserializer);
354 	//
355 	// return deserializer;
356 //	}
357 }