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