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(_pojoClass.getName()+"#"+setter.getPropertyName()+" not set from "+
191 (entry.getValue().getClass().getName())+"="+entry.getValue().toString());
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
227 public static class Setter
228 {
229 protected String _propertyName;
230 protected Method _method;
231 protected NumberType _numberType;
232 protected Class<?> _type;
233 protected Class<?> _componentType;
234
235 public Setter(String propertyName, Method method)
236 {
237 _propertyName = propertyName;
238 _method = method;
239 _type = method.getParameterTypes()[0];
240 _numberType = __numberTypes.get(_type);
241 if(_numberType==null && _type.isArray())
242 {
243 _componentType = _type.getComponentType();
244 _numberType = __numberTypes.get(_componentType);
245 }
246 }
247
248 public String getPropertyName()
249 {
250 return _propertyName;
251 }
252
253 public Method getMethod()
254 {
255 return _method;
256 }
257
258 public NumberType getNumberType()
259 {
260 return _numberType;
261 }
262
263 public Class<?> getType()
264 {
265 return _type;
266 }
267
268 public Class<?> getComponentType()
269 {
270 return _componentType;
271 }
272
273 public boolean isPropertyNumber()
274 {
275 return _numberType!=null;
276 }
277
278 public void invoke(Object obj, Object value) throws IllegalArgumentException,
279 IllegalAccessException, InvocationTargetException
280 {
281 if(value==null)
282 _method.invoke(obj, NULL_ARG);
283 else
284 invokeObject(obj, value);
285 }
286
287 protected void invokeObject(Object obj, Object value) throws IllegalArgumentException,
288 IllegalAccessException, InvocationTargetException
289 {
290
291 if (_type.isEnum())
292 {
293 if (value instanceof Enum)
294 _method.invoke(obj, new Object[]{value});
295 else
296 _method.invoke(obj, new Object[]{Enum.valueOf((Class<? extends Enum>)_type,value.toString())});
297 }
298 else if(_numberType!=null && value instanceof Number)
299 {
300 _method.invoke(obj, new Object[]{_numberType.getActualValue((Number)value)});
301 }
302 else if(_componentType!=null && value.getClass().isArray())
303 {
304 if(_numberType==null)
305 {
306 int len = Array.getLength(value);
307 Object array = Array.newInstance(_componentType, len);
308 try
309 {
310 System.arraycopy(value, 0, array, 0, len);
311 }
312 catch(Exception e)
313 {
314
315 Log.ignore(e);
316 _method.invoke(obj, new Object[]{value});
317 return;
318 }
319 _method.invoke(obj, new Object[]{array});
320 }
321 else
322 {
323 Object[] old = (Object[])value;
324 Object array = Array.newInstance(_componentType, old.length);
325 try
326 {
327 for(int i=0; i<old.length; i++)
328 Array.set(array, i, _numberType.getActualValue((Number)old[i]));
329 }
330 catch(Exception e)
331 {
332
333 Log.ignore(e);
334 _method.invoke(obj, new Object[]{value});
335 return;
336 }
337 _method.invoke(obj, new Object[]{array});
338 }
339 }
340 else
341 _method.invoke(obj, new Object[]{value});
342 }
343 }
344
345 public interface NumberType
346 {
347 public Object getActualValue(Number number);
348 }
349
350 public static final NumberType SHORT = new NumberType()
351 {
352 public Object getActualValue(Number number)
353 {
354 return new Short(number.shortValue());
355 }
356 };
357
358 public static final NumberType INTEGER = new NumberType()
359 {
360 public Object getActualValue(Number number)
361 {
362 return new Integer(number.intValue());
363 }
364 };
365
366 public static final NumberType FLOAT = new NumberType()
367 {
368 public Object getActualValue(Number number)
369 {
370 return new Float(number.floatValue());
371 }
372 };
373
374 public static final NumberType LONG = new NumberType()
375 {
376 public Object getActualValue(Number number)
377 {
378 return number instanceof Long ? number : new Long(number.longValue());
379 }
380 };
381
382 public static final NumberType DOUBLE = new NumberType()
383 {
384 public Object getActualValue(Number number)
385 {
386 return number instanceof Double ? number : new Double(number.doubleValue());
387 }
388 };
389
390 static
391 {
392 __numberTypes.put(Short.class, SHORT);
393 __numberTypes.put(Short.TYPE, SHORT);
394 __numberTypes.put(Integer.class, INTEGER);
395 __numberTypes.put(Integer.TYPE, INTEGER);
396 __numberTypes.put(Long.class, LONG);
397 __numberTypes.put(Long.TYPE, LONG);
398 __numberTypes.put(Float.class, FLOAT);
399 __numberTypes.put(Float.TYPE, FLOAT);
400 __numberTypes.put(Double.class, DOUBLE);
401 __numberTypes.put(Double.TYPE, DOUBLE);
402 }
403 }