View Javadoc

1   //========================================================================
2   //Copyright 2009 Mort Bay Consulting Pty. Ltd.
3   //------------------------------------------------------------------------
4   //Licensed under the Apache License, Version 2.0 (the "License");
5   //you may not use this file except in compliance with the License.
6   //You may obtain a copy of the License at 
7   //http://www.apache.org/licenses/LICENSE-2.0
8   //Unless required by applicable law or agreed to in writing, software
9   //distributed under the License is distributed on an "AS IS" BASIS,
10  //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  //See the License for the specific language governing permissions and
12  //limitations under the License.
13  //========================================================================
14  
15  package org.mortbay.util.ajax;
16  
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.mortbay.log.Log;
28  import org.mortbay.util.ajax.JSON.Output;
29  /* ------------------------------------------------------------ */
30  /**
31   * Converts POJOs to JSON and vice versa.
32   * The key difference:
33   *  - returns the actual object from Convertor.fromJSON (JSONObjectConverter returns a Map)
34   *  - the getters/setters are resolved at initialization (JSONObjectConverter resolves it at runtime)
35   *  - correctly sets the number fields
36   * 
37   * @author dyu
38   *
39   */
40  public class JSONPojoConvertor implements JSON.Convertor
41  {
42      
43      private static final Object[] __getterArg = new Object[]{};
44      private static final Map/*<Class<?>, NumberType>*/  __numberTypes = new HashMap/*<Class<?>, NumberType>*/();
45      
46      public static NumberType getNumberType(Class clazz)
47      {
48          return (NumberType)__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)null, true);
60      }
61      
62      public JSONPojoConvertor(Class pojoClass, String[] excluded)
63      {
64          this(pojoClass, new HashSet(Arrays.asList(excluded)), true);
65      }
66      
67      public JSONPojoConvertor(Class pojoClass, Set excluded)
68      {
69          this(pojoClass, excluded, true);
70      }
71      
72      public JSONPojoConvertor(Class pojoClass, Set 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)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"))
102                                 name=name.substring(2,3).toLowerCase()+name.substring(3);
103                             else if (name.startsWith("get"))
104                                 name=name.substring(3,4).toLowerCase()+name.substring(4);
105                             else 
106                                 break;
107                             if(includeField(name, m))
108                                 _getters.put(name, m);
109                         }
110                         break;
111                     case 1:
112                         if (name.startsWith("set"))
113                         {
114                             name=name.substring(3,4).toLowerCase()+name.substring(4);
115                             if(includeField(name, m))
116                                 _setters.put(name, new Setter(name, m));
117                         }
118                         break;                
119                 }
120             }
121         }
122     }
123 
124     /* ------------------------------------------------------------ */
125     protected boolean includeField(String name, Method m)
126     {
127         return _excluded==null || !_excluded.contains(name);
128     }
129     
130     /* ------------------------------------------------------------ */
131     protected int getExcludedCount()
132     {
133         return _excluded==null ? 0 : _excluded.size();
134     }
135 
136     /* ------------------------------------------------------------ */
137     public Object fromJSON(Map object)
138     {        
139         Object obj = null;
140         try
141         {
142             obj = _pojoClass.newInstance();
143         }
144         catch(Exception e)
145         {
146             // TODO return Map instead?
147             throw new RuntimeException(e);
148         }
149         
150         for(Iterator iterator = object.entrySet().iterator(); iterator.hasNext();)
151         {
152             Map.Entry entry = (Map.Entry)iterator.next();
153             Setter setter = (Setter)_setters.get(entry.getKey());
154             if(setter!=null)
155             {
156                 try
157                 {
158                     setter.invoke(obj, entry.getValue());                    
159                 }
160                 catch(Exception e)
161                 {
162                     // TODO throw exception?
163                     Log.warn("{} property '{}' not set. (errors)", _pojoClass.getName(), 
164                             setter.getPropertyName());                    
165                 }
166             }
167         }
168         
169         return obj;
170     }
171 
172     /* ------------------------------------------------------------ */
173     public void toJSON(Object obj, Output out)
174     {
175         if(_fromJSON)
176             out.addClass(_pojoClass);
177         for(Iterator iterator = _getters.entrySet().iterator(); iterator.hasNext();)
178         {
179             Map.Entry entry = (Map.Entry)iterator.next();
180             try
181             {
182                 out.add((String)entry.getKey(), ((Method)entry.getValue()).invoke(obj, 
183                         __getterArg));                    
184             }
185             catch(Exception e)
186             {
187                 // TODO throw exception?
188                 Log.warn("{} property '{}' excluded. (errors)", _pojoClass.getName(), 
189                         entry.getKey());                
190             }
191         }        
192     }
193     
194     public static class Setter
195     {
196         private String _propertyName;
197         private Method _method;
198         private NumberType _numberType;
199         
200         public Setter(String propertyName, Method method)
201         {
202             _propertyName = propertyName;
203             _method = method;
204             _numberType = (NumberType)__numberTypes.get(method.getParameterTypes()[0]);
205         }
206         
207         public String getPropertyName()
208         {
209             return _propertyName;
210         }
211         
212         public Method getMethod()
213         {
214             return _method;
215         }
216         
217         public NumberType getNumberType()
218         {
219             return _numberType;
220         }
221         
222         public boolean isPropertyNumber()
223         {
224             return _numberType!=null;
225         }
226         
227         public void invoke(Object obj, Object value) throws IllegalArgumentException, 
228             IllegalAccessException, InvocationTargetException
229         {
230             if(_numberType!=null && value instanceof Number)
231                 _method.invoke(obj, new Object[]{_numberType.getActualValue((Number)value)});
232             else
233                 _method.invoke(obj, new Object[]{value});
234         }
235     }
236     
237     public interface NumberType
238     {        
239         public Object getActualValue(Number number);     
240     }
241     
242     public static final NumberType SHORT = new NumberType()
243     {
244         public Object getActualValue(Number number)
245         {            
246             return new Short(number.shortValue());
247         } 
248     };
249 
250     public static final NumberType INTEGER = new NumberType()
251     {
252         public Object getActualValue(Number number)
253         {            
254             return new Integer(number.intValue());
255         }
256     };
257     
258     public static final NumberType FLOAT = new NumberType()
259     {
260         public Object getActualValue(Number number)
261         {            
262             return new Float(number.floatValue());
263         }      
264     };
265 
266     public static final NumberType LONG = new NumberType()
267     {
268         public Object getActualValue(Number number)
269         {            
270             return number instanceof Long ? number : new Long(number.longValue());
271         }     
272     };
273 
274     public static final NumberType DOUBLE = new NumberType()
275     {
276         public Object getActualValue(Number number)
277         {            
278             return number instanceof Double ? number : new Double(number.doubleValue());
279         }       
280     };
281 
282     static
283     {
284         __numberTypes.put(Short.class, SHORT);
285         __numberTypes.put(Short.TYPE, SHORT);
286         __numberTypes.put(Integer.class, INTEGER);
287         __numberTypes.put(Integer.TYPE, INTEGER);
288         __numberTypes.put(Long.class, LONG);
289         __numberTypes.put(Long.TYPE, LONG);
290         __numberTypes.put(Float.class, FLOAT);
291         __numberTypes.put(Float.TYPE, FLOAT);
292         __numberTypes.put(Double.class, DOUBLE);
293         __numberTypes.put(Double.TYPE, DOUBLE);
294     }
295 }