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