Mercurial Hosting > luan
comparison src/org/eclipse/jetty/util/ajax/JSONPojoConvertor.java @ 802:3428c60d7cfc
replace jetty jars with source
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Wed, 07 Sep 2016 21:15:48 -0600 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
801:6a21393191c1 | 802:3428c60d7cfc |
---|---|
1 // | |
2 // ======================================================================== | |
3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. | |
4 // ------------------------------------------------------------------------ | |
5 // All rights reserved. This program and the accompanying materials | |
6 // are made available under the terms of the Eclipse Public License v1.0 | |
7 // and Apache License v2.0 which accompanies this distribution. | |
8 // | |
9 // The Eclipse Public License is available at | |
10 // http://www.eclipse.org/legal/epl-v10.html | |
11 // | |
12 // The Apache License v2.0 is available at | |
13 // http://www.opensource.org/licenses/apache2.0.php | |
14 // | |
15 // You may elect to redistribute this code under either of these licenses. | |
16 // ======================================================================== | |
17 // | |
18 | |
19 package org.eclipse.jetty.util.ajax; | |
20 | |
21 import java.lang.reflect.Array; | |
22 import java.lang.reflect.InvocationTargetException; | |
23 import java.lang.reflect.Method; | |
24 import java.lang.reflect.Modifier; | |
25 import java.util.Arrays; | |
26 import java.util.HashMap; | |
27 import java.util.HashSet; | |
28 import java.util.Iterator; | |
29 import java.util.Locale; | |
30 import java.util.Map; | |
31 import java.util.Set; | |
32 | |
33 import org.eclipse.jetty.util.ajax.JSON.Output; | |
34 import org.eclipse.jetty.util.log.Log; | |
35 import org.eclipse.jetty.util.log.Logger; | |
36 /** | |
37 * Converts POJOs to JSON and vice versa. | |
38 * The key difference: | |
39 * - returns the actual object from Convertor.fromJSON (JSONObjectConverter returns a Map) | |
40 * - the getters/setters are resolved at initialization (JSONObjectConverter resolves it at runtime) | |
41 * - correctly sets the number fields | |
42 * | |
43 */ | |
44 public class JSONPojoConvertor implements JSON.Convertor | |
45 { | |
46 private static final Logger LOG = Log.getLogger(JSONPojoConvertor.class); | |
47 public static final Object[] GETTER_ARG = new Object[]{}, NULL_ARG = new Object[]{null}; | |
48 private static final Map<Class<?>, NumberType> __numberTypes = new HashMap<Class<?>, NumberType>(); | |
49 | |
50 public static NumberType getNumberType(Class<?> clazz) | |
51 { | |
52 return __numberTypes.get(clazz); | |
53 } | |
54 | |
55 protected boolean _fromJSON; | |
56 protected Class<?> _pojoClass; | |
57 protected Map<String,Method> _getters = new HashMap<String,Method>(); | |
58 protected Map<String,Setter> _setters = new HashMap<String,Setter>(); | |
59 protected Set<String> _excluded; | |
60 | |
61 /** | |
62 * @param pojoClass The class to convert | |
63 */ | |
64 public JSONPojoConvertor(Class<?> pojoClass) | |
65 { | |
66 this(pojoClass, (Set<String>)null, true); | |
67 } | |
68 | |
69 /** | |
70 * @param pojoClass The class to convert | |
71 * @param excluded The fields to exclude | |
72 */ | |
73 public JSONPojoConvertor(Class<?> pojoClass, String[] excluded) | |
74 { | |
75 this(pojoClass, new HashSet<String>(Arrays.asList(excluded)), true); | |
76 } | |
77 | |
78 /** | |
79 * @param pojoClass The class to convert | |
80 * @param excluded The fields to exclude | |
81 */ | |
82 public JSONPojoConvertor(Class<?> pojoClass, Set<String> excluded) | |
83 { | |
84 this(pojoClass, excluded, true); | |
85 } | |
86 | |
87 /** | |
88 * @param pojoClass The class to convert | |
89 * @param excluded The fields to exclude | |
90 * @param fromJSON If true, add a class field to the JSON | |
91 */ | |
92 public JSONPojoConvertor(Class<?> pojoClass, Set<String> excluded, boolean fromJSON) | |
93 { | |
94 _pojoClass = pojoClass; | |
95 _excluded = excluded; | |
96 _fromJSON = fromJSON; | |
97 init(); | |
98 } | |
99 | |
100 /** | |
101 * @param pojoClass The class to convert | |
102 * @param fromJSON If true, add a class field to the JSON | |
103 */ | |
104 public JSONPojoConvertor(Class<?> pojoClass, boolean fromJSON) | |
105 { | |
106 this(pojoClass, (Set<String>)null, fromJSON); | |
107 } | |
108 | |
109 /* ------------------------------------------------------------ */ | |
110 protected void init() | |
111 { | |
112 Method[] methods = _pojoClass.getMethods(); | |
113 for (int i=0;i<methods.length;i++) | |
114 { | |
115 Method m=methods[i]; | |
116 if (!Modifier.isStatic(m.getModifiers()) && m.getDeclaringClass()!=Object.class) | |
117 { | |
118 String name=m.getName(); | |
119 switch(m.getParameterTypes().length) | |
120 { | |
121 case 0: | |
122 | |
123 if(m.getReturnType()!=null) | |
124 { | |
125 if (name.startsWith("is") && name.length()>2) | |
126 name=name.substring(2,3).toLowerCase(Locale.ENGLISH)+name.substring(3); | |
127 else if (name.startsWith("get") && name.length()>3) | |
128 name=name.substring(3,4).toLowerCase(Locale.ENGLISH)+name.substring(4); | |
129 else | |
130 break; | |
131 if(includeField(name, m)) | |
132 addGetter(name, m); | |
133 } | |
134 break; | |
135 case 1: | |
136 if (name.startsWith("set") && name.length()>3) | |
137 { | |
138 name=name.substring(3,4).toLowerCase(Locale.ENGLISH)+name.substring(4); | |
139 if(includeField(name, m)) | |
140 addSetter(name, m); | |
141 } | |
142 break; | |
143 } | |
144 } | |
145 } | |
146 } | |
147 | |
148 /* ------------------------------------------------------------ */ | |
149 protected void addGetter(String name, Method method) | |
150 { | |
151 _getters.put(name, method); | |
152 } | |
153 | |
154 /* ------------------------------------------------------------ */ | |
155 protected void addSetter(String name, Method method) | |
156 { | |
157 _setters.put(name, new Setter(name, method)); | |
158 } | |
159 | |
160 /* ------------------------------------------------------------ */ | |
161 protected Setter getSetter(String name) | |
162 { | |
163 return _setters.get(name); | |
164 } | |
165 | |
166 /* ------------------------------------------------------------ */ | |
167 protected boolean includeField(String name, Method m) | |
168 { | |
169 return _excluded==null || !_excluded.contains(name); | |
170 } | |
171 | |
172 /* ------------------------------------------------------------ */ | |
173 protected int getExcludedCount() | |
174 { | |
175 return _excluded==null ? 0 : _excluded.size(); | |
176 } | |
177 | |
178 /* ------------------------------------------------------------ */ | |
179 public Object fromJSON(Map object) | |
180 { | |
181 Object obj = null; | |
182 try | |
183 { | |
184 obj = _pojoClass.newInstance(); | |
185 } | |
186 catch(Exception e) | |
187 { | |
188 // TODO return Map instead? | |
189 throw new RuntimeException(e); | |
190 } | |
191 | |
192 setProps(obj, object); | |
193 return obj; | |
194 } | |
195 | |
196 /* ------------------------------------------------------------ */ | |
197 public int setProps(Object obj, Map<?,?> props) | |
198 { | |
199 int count = 0; | |
200 for(Iterator<?> iterator = props.entrySet().iterator(); iterator.hasNext();) | |
201 { | |
202 Map.Entry<?, ?> entry = (Map.Entry<?,?>) iterator.next(); | |
203 Setter setter = getSetter((String)entry.getKey()); | |
204 if(setter!=null) | |
205 { | |
206 try | |
207 { | |
208 setter.invoke(obj, entry.getValue()); | |
209 count++; | |
210 } | |
211 catch(Exception e) | |
212 { | |
213 // TODO throw exception? | |
214 LOG.warn(_pojoClass.getName()+"#"+setter.getPropertyName()+" not set from "+ | |
215 (entry.getValue().getClass().getName())+"="+entry.getValue().toString()); | |
216 log(e); | |
217 } | |
218 } | |
219 } | |
220 return count; | |
221 } | |
222 | |
223 /* ------------------------------------------------------------ */ | |
224 public void toJSON(Object obj, Output out) | |
225 { | |
226 if(_fromJSON) | |
227 out.addClass(_pojoClass); | |
228 for(Map.Entry<String,Method> entry : _getters.entrySet()) | |
229 { | |
230 try | |
231 { | |
232 out.add(entry.getKey(), entry.getValue().invoke(obj, GETTER_ARG)); | |
233 } | |
234 catch(Exception e) | |
235 { | |
236 // TODO throw exception? | |
237 LOG.warn("{} property '{}' excluded. (errors)", _pojoClass.getName(), | |
238 entry.getKey()); | |
239 log(e); | |
240 } | |
241 } | |
242 } | |
243 | |
244 /* ------------------------------------------------------------ */ | |
245 protected void log(Throwable t) | |
246 { | |
247 LOG.ignore(t); | |
248 } | |
249 | |
250 /* ------------------------------------------------------------ */ | |
251 public static class Setter | |
252 { | |
253 protected String _propertyName; | |
254 protected Method _setter; | |
255 protected NumberType _numberType; | |
256 protected Class<?> _type; | |
257 protected Class<?> _componentType; | |
258 | |
259 public Setter(String propertyName, Method method) | |
260 { | |
261 _propertyName = propertyName; | |
262 _setter = method; | |
263 _type = method.getParameterTypes()[0]; | |
264 _numberType = __numberTypes.get(_type); | |
265 if(_numberType==null && _type.isArray()) | |
266 { | |
267 _componentType = _type.getComponentType(); | |
268 _numberType = __numberTypes.get(_componentType); | |
269 } | |
270 } | |
271 | |
272 public String getPropertyName() | |
273 { | |
274 return _propertyName; | |
275 } | |
276 | |
277 public Method getMethod() | |
278 { | |
279 return _setter; | |
280 } | |
281 | |
282 public NumberType getNumberType() | |
283 { | |
284 return _numberType; | |
285 } | |
286 | |
287 public Class<?> getType() | |
288 { | |
289 return _type; | |
290 } | |
291 | |
292 public Class<?> getComponentType() | |
293 { | |
294 return _componentType; | |
295 } | |
296 | |
297 public boolean isPropertyNumber() | |
298 { | |
299 return _numberType!=null; | |
300 } | |
301 | |
302 public void invoke(Object obj, Object value) throws IllegalArgumentException, | |
303 IllegalAccessException, InvocationTargetException | |
304 { | |
305 if(value==null) | |
306 _setter.invoke(obj, NULL_ARG); | |
307 else | |
308 invokeObject(obj, value); | |
309 } | |
310 | |
311 protected void invokeObject(Object obj, Object value) throws IllegalArgumentException, | |
312 IllegalAccessException, InvocationTargetException | |
313 { | |
314 | |
315 if (_type.isEnum()) | |
316 { | |
317 if (value instanceof Enum) | |
318 _setter.invoke(obj, new Object[]{value}); | |
319 else | |
320 _setter.invoke(obj, new Object[]{Enum.valueOf((Class<? extends Enum>)_type,value.toString())}); | |
321 } | |
322 else if(_numberType!=null && value instanceof Number) | |
323 { | |
324 _setter.invoke(obj, new Object[]{_numberType.getActualValue((Number)value)}); | |
325 } | |
326 else if (Character.TYPE.equals(_type) || Character.class.equals(_type)) | |
327 { | |
328 _setter.invoke(obj, new Object[]{String.valueOf(value).charAt(0)}); | |
329 } | |
330 else if(_componentType!=null && value.getClass().isArray()) | |
331 { | |
332 if(_numberType==null) | |
333 { | |
334 int len = Array.getLength(value); | |
335 Object array = Array.newInstance(_componentType, len); | |
336 try | |
337 { | |
338 System.arraycopy(value, 0, array, 0, len); | |
339 } | |
340 catch(Exception e) | |
341 { | |
342 // unusual array with multiple types | |
343 LOG.ignore(e); | |
344 _setter.invoke(obj, new Object[]{value}); | |
345 return; | |
346 } | |
347 _setter.invoke(obj, new Object[]{array}); | |
348 } | |
349 else | |
350 { | |
351 Object[] old = (Object[])value; | |
352 Object array = Array.newInstance(_componentType, old.length); | |
353 try | |
354 { | |
355 for(int i=0; i<old.length; i++) | |
356 Array.set(array, i, _numberType.getActualValue((Number)old[i])); | |
357 } | |
358 catch(Exception e) | |
359 { | |
360 // unusual array with multiple types | |
361 LOG.ignore(e); | |
362 _setter.invoke(obj, new Object[]{value}); | |
363 return; | |
364 } | |
365 _setter.invoke(obj, new Object[]{array}); | |
366 } | |
367 } | |
368 else | |
369 _setter.invoke(obj, new Object[]{value}); | |
370 } | |
371 } | |
372 | |
373 public interface NumberType | |
374 { | |
375 public Object getActualValue(Number number); | |
376 } | |
377 | |
378 public static final NumberType SHORT = new NumberType() | |
379 { | |
380 public Object getActualValue(Number number) | |
381 { | |
382 return new Short(number.shortValue()); | |
383 } | |
384 }; | |
385 | |
386 public static final NumberType INTEGER = new NumberType() | |
387 { | |
388 public Object getActualValue(Number number) | |
389 { | |
390 return new Integer(number.intValue()); | |
391 } | |
392 }; | |
393 | |
394 public static final NumberType FLOAT = new NumberType() | |
395 { | |
396 public Object getActualValue(Number number) | |
397 { | |
398 return new Float(number.floatValue()); | |
399 } | |
400 }; | |
401 | |
402 public static final NumberType LONG = new NumberType() | |
403 { | |
404 public Object getActualValue(Number number) | |
405 { | |
406 return number instanceof Long ? number : new Long(number.longValue()); | |
407 } | |
408 }; | |
409 | |
410 public static final NumberType DOUBLE = new NumberType() | |
411 { | |
412 public Object getActualValue(Number number) | |
413 { | |
414 return number instanceof Double ? number : new Double(number.doubleValue()); | |
415 } | |
416 }; | |
417 | |
418 static | |
419 { | |
420 __numberTypes.put(Short.class, SHORT); | |
421 __numberTypes.put(Short.TYPE, SHORT); | |
422 __numberTypes.put(Integer.class, INTEGER); | |
423 __numberTypes.put(Integer.TYPE, INTEGER); | |
424 __numberTypes.put(Long.class, LONG); | |
425 __numberTypes.put(Long.TYPE, LONG); | |
426 __numberTypes.put(Float.class, FLOAT); | |
427 __numberTypes.put(Float.TYPE, FLOAT); | |
428 __numberTypes.put(Double.class, DOUBLE); | |
429 __numberTypes.put(Double.TYPE, DOUBLE); | |
430 } | |
431 } |