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 import org.eclipse.jetty.util.log.Logger;
30
31
32
33
34
35
36
37
38 public class JSONPojoConvertor implements JSON.Convertor
39 {
40 private static final Logger LOG = Log.getLogger(JSONPojoConvertor.class);
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 _setter;
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 _setter = 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 _setter;
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 _setter.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 _setter.invoke(obj, new Object[]{value});
313 else
314 _setter.invoke(obj, new Object[]{Enum.valueOf((Class<? extends Enum>)_type,value.toString())});
315 }
316 else if(_numberType!=null && value instanceof Number)
317 {
318 _setter.invoke(obj, new Object[]{_numberType.getActualValue((Number)value)});
319 }
320 else if (Character.TYPE.equals(_type) || Character.class.equals(_type))
321 {
322 _setter.invoke(obj, new Object[]{String.valueOf(value).charAt(0)});
323 }
324 else if(_componentType!=null && value.getClass().isArray())
325 {
326 if(_numberType==null)
327 {
328 int len = Array.getLength(value);
329 Object array = Array.newInstance(_componentType, len);
330 try
331 {
332 System.arraycopy(value, 0, array, 0, len);
333 }
334 catch(Exception e)
335 {
336
337 LOG.ignore(e);
338 _setter.invoke(obj, new Object[]{value});
339 return;
340 }
341 _setter.invoke(obj, new Object[]{array});
342 }
343 else
344 {
345 Object[] old = (Object[])value;
346 Object array = Array.newInstance(_componentType, old.length);
347 try
348 {
349 for(int i=0; i<old.length; i++)
350 Array.set(array, i, _numberType.getActualValue((Number)old[i]));
351 }
352 catch(Exception e)
353 {
354
355 LOG.ignore(e);
356 _setter.invoke(obj, new Object[]{value});
357 return;
358 }
359 _setter.invoke(obj, new Object[]{array});
360 }
361 }
362 else
363 _setter.invoke(obj, new Object[]{value});
364 }
365 }
366
367 public interface NumberType
368 {
369 public Object getActualValue(Number number);
370 }
371
372 public static final NumberType SHORT = new NumberType()
373 {
374 public Object getActualValue(Number number)
375 {
376 return new Short(number.shortValue());
377 }
378 };
379
380 public static final NumberType INTEGER = new NumberType()
381 {
382 public Object getActualValue(Number number)
383 {
384 return new Integer(number.intValue());
385 }
386 };
387
388 public static final NumberType FLOAT = new NumberType()
389 {
390 public Object getActualValue(Number number)
391 {
392 return new Float(number.floatValue());
393 }
394 };
395
396 public static final NumberType LONG = new NumberType()
397 {
398 public Object getActualValue(Number number)
399 {
400 return number instanceof Long ? number : new Long(number.longValue());
401 }
402 };
403
404 public static final NumberType DOUBLE = new NumberType()
405 {
406 public Object getActualValue(Number number)
407 {
408 return number instanceof Double ? number : new Double(number.doubleValue());
409 }
410 };
411
412 static
413 {
414 __numberTypes.put(Short.class, SHORT);
415 __numberTypes.put(Short.TYPE, SHORT);
416 __numberTypes.put(Integer.class, INTEGER);
417 __numberTypes.put(Integer.TYPE, INTEGER);
418 __numberTypes.put(Long.class, LONG);
419 __numberTypes.put(Long.TYPE, LONG);
420 __numberTypes.put(Float.class, FLOAT);
421 __numberTypes.put(Float.TYPE, FLOAT);
422 __numberTypes.put(Double.class, DOUBLE);
423 __numberTypes.put(Double.TYPE, DOUBLE);
424 }
425 }