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