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