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