Mercurial Hosting > luan
comparison src/org/eclipse/jetty/util/ajax/JSONPojoConvertor.java @ 802:3428c60d7cfc
replace jetty jars with source
| author | Franklin Schmidt <fschmidt@gmail.com> |
|---|---|
| date | Wed, 07 Sep 2016 21:15:48 -0600 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 801:6a21393191c1 | 802:3428c60d7cfc |
|---|---|
| 1 // | |
| 2 // ======================================================================== | |
| 3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. | |
| 4 // ------------------------------------------------------------------------ | |
| 5 // All rights reserved. This program and the accompanying materials | |
| 6 // are made available under the terms of the Eclipse Public License v1.0 | |
| 7 // and Apache License v2.0 which accompanies this distribution. | |
| 8 // | |
| 9 // The Eclipse Public License is available at | |
| 10 // http://www.eclipse.org/legal/epl-v10.html | |
| 11 // | |
| 12 // The Apache License v2.0 is available at | |
| 13 // http://www.opensource.org/licenses/apache2.0.php | |
| 14 // | |
| 15 // You may elect to redistribute this code under either of these licenses. | |
| 16 // ======================================================================== | |
| 17 // | |
| 18 | |
| 19 package org.eclipse.jetty.util.ajax; | |
| 20 | |
| 21 import java.lang.reflect.Array; | |
| 22 import java.lang.reflect.InvocationTargetException; | |
| 23 import java.lang.reflect.Method; | |
| 24 import java.lang.reflect.Modifier; | |
| 25 import java.util.Arrays; | |
| 26 import java.util.HashMap; | |
| 27 import java.util.HashSet; | |
| 28 import java.util.Iterator; | |
| 29 import java.util.Locale; | |
| 30 import java.util.Map; | |
| 31 import java.util.Set; | |
| 32 | |
| 33 import org.eclipse.jetty.util.ajax.JSON.Output; | |
| 34 import org.eclipse.jetty.util.log.Log; | |
| 35 import org.eclipse.jetty.util.log.Logger; | |
| 36 /** | |
| 37 * Converts POJOs to JSON and vice versa. | |
| 38 * The key difference: | |
| 39 * - returns the actual object from Convertor.fromJSON (JSONObjectConverter returns a Map) | |
| 40 * - the getters/setters are resolved at initialization (JSONObjectConverter resolves it at runtime) | |
| 41 * - correctly sets the number fields | |
| 42 * | |
| 43 */ | |
| 44 public class JSONPojoConvertor implements JSON.Convertor | |
| 45 { | |
| 46 private static final Logger LOG = Log.getLogger(JSONPojoConvertor.class); | |
| 47 public static final Object[] GETTER_ARG = new Object[]{}, NULL_ARG = new Object[]{null}; | |
| 48 private static final Map<Class<?>, NumberType> __numberTypes = new HashMap<Class<?>, NumberType>(); | |
| 49 | |
| 50 public static NumberType getNumberType(Class<?> clazz) | |
| 51 { | |
| 52 return __numberTypes.get(clazz); | |
| 53 } | |
| 54 | |
| 55 protected boolean _fromJSON; | |
| 56 protected Class<?> _pojoClass; | |
| 57 protected Map<String,Method> _getters = new HashMap<String,Method>(); | |
| 58 protected Map<String,Setter> _setters = new HashMap<String,Setter>(); | |
| 59 protected Set<String> _excluded; | |
| 60 | |
| 61 /** | |
| 62 * @param pojoClass The class to convert | |
| 63 */ | |
| 64 public JSONPojoConvertor(Class<?> pojoClass) | |
| 65 { | |
| 66 this(pojoClass, (Set<String>)null, true); | |
| 67 } | |
| 68 | |
| 69 /** | |
| 70 * @param pojoClass The class to convert | |
| 71 * @param excluded The fields to exclude | |
| 72 */ | |
| 73 public JSONPojoConvertor(Class<?> pojoClass, String[] excluded) | |
| 74 { | |
| 75 this(pojoClass, new HashSet<String>(Arrays.asList(excluded)), true); | |
| 76 } | |
| 77 | |
| 78 /** | |
| 79 * @param pojoClass The class to convert | |
| 80 * @param excluded The fields to exclude | |
| 81 */ | |
| 82 public JSONPojoConvertor(Class<?> pojoClass, Set<String> excluded) | |
| 83 { | |
| 84 this(pojoClass, excluded, true); | |
| 85 } | |
| 86 | |
| 87 /** | |
| 88 * @param pojoClass The class to convert | |
| 89 * @param excluded The fields to exclude | |
| 90 * @param fromJSON If true, add a class field to the JSON | |
| 91 */ | |
| 92 public JSONPojoConvertor(Class<?> pojoClass, Set<String> excluded, boolean fromJSON) | |
| 93 { | |
| 94 _pojoClass = pojoClass; | |
| 95 _excluded = excluded; | |
| 96 _fromJSON = fromJSON; | |
| 97 init(); | |
| 98 } | |
| 99 | |
| 100 /** | |
| 101 * @param pojoClass The class to convert | |
| 102 * @param fromJSON If true, add a class field to the JSON | |
| 103 */ | |
| 104 public JSONPojoConvertor(Class<?> pojoClass, boolean fromJSON) | |
| 105 { | |
| 106 this(pojoClass, (Set<String>)null, fromJSON); | |
| 107 } | |
| 108 | |
| 109 /* ------------------------------------------------------------ */ | |
| 110 protected void init() | |
| 111 { | |
| 112 Method[] methods = _pojoClass.getMethods(); | |
| 113 for (int i=0;i<methods.length;i++) | |
| 114 { | |
| 115 Method m=methods[i]; | |
| 116 if (!Modifier.isStatic(m.getModifiers()) && m.getDeclaringClass()!=Object.class) | |
| 117 { | |
| 118 String name=m.getName(); | |
| 119 switch(m.getParameterTypes().length) | |
| 120 { | |
| 121 case 0: | |
| 122 | |
| 123 if(m.getReturnType()!=null) | |
| 124 { | |
| 125 if (name.startsWith("is") && name.length()>2) | |
| 126 name=name.substring(2,3).toLowerCase(Locale.ENGLISH)+name.substring(3); | |
| 127 else if (name.startsWith("get") && name.length()>3) | |
| 128 name=name.substring(3,4).toLowerCase(Locale.ENGLISH)+name.substring(4); | |
| 129 else | |
| 130 break; | |
| 131 if(includeField(name, m)) | |
| 132 addGetter(name, m); | |
| 133 } | |
| 134 break; | |
| 135 case 1: | |
| 136 if (name.startsWith("set") && name.length()>3) | |
| 137 { | |
| 138 name=name.substring(3,4).toLowerCase(Locale.ENGLISH)+name.substring(4); | |
| 139 if(includeField(name, m)) | |
| 140 addSetter(name, m); | |
| 141 } | |
| 142 break; | |
| 143 } | |
| 144 } | |
| 145 } | |
| 146 } | |
| 147 | |
| 148 /* ------------------------------------------------------------ */ | |
| 149 protected void addGetter(String name, Method method) | |
| 150 { | |
| 151 _getters.put(name, method); | |
| 152 } | |
| 153 | |
| 154 /* ------------------------------------------------------------ */ | |
| 155 protected void addSetter(String name, Method method) | |
| 156 { | |
| 157 _setters.put(name, new Setter(name, method)); | |
| 158 } | |
| 159 | |
| 160 /* ------------------------------------------------------------ */ | |
| 161 protected Setter getSetter(String name) | |
| 162 { | |
| 163 return _setters.get(name); | |
| 164 } | |
| 165 | |
| 166 /* ------------------------------------------------------------ */ | |
| 167 protected boolean includeField(String name, Method m) | |
| 168 { | |
| 169 return _excluded==null || !_excluded.contains(name); | |
| 170 } | |
| 171 | |
| 172 /* ------------------------------------------------------------ */ | |
| 173 protected int getExcludedCount() | |
| 174 { | |
| 175 return _excluded==null ? 0 : _excluded.size(); | |
| 176 } | |
| 177 | |
| 178 /* ------------------------------------------------------------ */ | |
| 179 public Object fromJSON(Map object) | |
| 180 { | |
| 181 Object obj = null; | |
| 182 try | |
| 183 { | |
| 184 obj = _pojoClass.newInstance(); | |
| 185 } | |
| 186 catch(Exception e) | |
| 187 { | |
| 188 // TODO return Map instead? | |
| 189 throw new RuntimeException(e); | |
| 190 } | |
| 191 | |
| 192 setProps(obj, object); | |
| 193 return obj; | |
| 194 } | |
| 195 | |
| 196 /* ------------------------------------------------------------ */ | |
| 197 public int setProps(Object obj, Map<?,?> props) | |
| 198 { | |
| 199 int count = 0; | |
| 200 for(Iterator<?> iterator = props.entrySet().iterator(); iterator.hasNext();) | |
| 201 { | |
| 202 Map.Entry<?, ?> entry = (Map.Entry<?,?>) iterator.next(); | |
| 203 Setter setter = getSetter((String)entry.getKey()); | |
| 204 if(setter!=null) | |
| 205 { | |
| 206 try | |
| 207 { | |
| 208 setter.invoke(obj, entry.getValue()); | |
| 209 count++; | |
| 210 } | |
| 211 catch(Exception e) | |
| 212 { | |
| 213 // TODO throw exception? | |
| 214 LOG.warn(_pojoClass.getName()+"#"+setter.getPropertyName()+" not set from "+ | |
| 215 (entry.getValue().getClass().getName())+"="+entry.getValue().toString()); | |
| 216 log(e); | |
| 217 } | |
| 218 } | |
| 219 } | |
| 220 return count; | |
| 221 } | |
| 222 | |
| 223 /* ------------------------------------------------------------ */ | |
| 224 public void toJSON(Object obj, Output out) | |
| 225 { | |
| 226 if(_fromJSON) | |
| 227 out.addClass(_pojoClass); | |
| 228 for(Map.Entry<String,Method> entry : _getters.entrySet()) | |
| 229 { | |
| 230 try | |
| 231 { | |
| 232 out.add(entry.getKey(), entry.getValue().invoke(obj, GETTER_ARG)); | |
| 233 } | |
| 234 catch(Exception e) | |
| 235 { | |
| 236 // TODO throw exception? | |
| 237 LOG.warn("{} property '{}' excluded. (errors)", _pojoClass.getName(), | |
| 238 entry.getKey()); | |
| 239 log(e); | |
| 240 } | |
| 241 } | |
| 242 } | |
| 243 | |
| 244 /* ------------------------------------------------------------ */ | |
| 245 protected void log(Throwable t) | |
| 246 { | |
| 247 LOG.ignore(t); | |
| 248 } | |
| 249 | |
| 250 /* ------------------------------------------------------------ */ | |
| 251 public static class Setter | |
| 252 { | |
| 253 protected String _propertyName; | |
| 254 protected Method _setter; | |
| 255 protected NumberType _numberType; | |
| 256 protected Class<?> _type; | |
| 257 protected Class<?> _componentType; | |
| 258 | |
| 259 public Setter(String propertyName, Method method) | |
| 260 { | |
| 261 _propertyName = propertyName; | |
| 262 _setter = method; | |
| 263 _type = method.getParameterTypes()[0]; | |
| 264 _numberType = __numberTypes.get(_type); | |
| 265 if(_numberType==null && _type.isArray()) | |
| 266 { | |
| 267 _componentType = _type.getComponentType(); | |
| 268 _numberType = __numberTypes.get(_componentType); | |
| 269 } | |
| 270 } | |
| 271 | |
| 272 public String getPropertyName() | |
| 273 { | |
| 274 return _propertyName; | |
| 275 } | |
| 276 | |
| 277 public Method getMethod() | |
| 278 { | |
| 279 return _setter; | |
| 280 } | |
| 281 | |
| 282 public NumberType getNumberType() | |
| 283 { | |
| 284 return _numberType; | |
| 285 } | |
| 286 | |
| 287 public Class<?> getType() | |
| 288 { | |
| 289 return _type; | |
| 290 } | |
| 291 | |
| 292 public Class<?> getComponentType() | |
| 293 { | |
| 294 return _componentType; | |
| 295 } | |
| 296 | |
| 297 public boolean isPropertyNumber() | |
| 298 { | |
| 299 return _numberType!=null; | |
| 300 } | |
| 301 | |
| 302 public void invoke(Object obj, Object value) throws IllegalArgumentException, | |
| 303 IllegalAccessException, InvocationTargetException | |
| 304 { | |
| 305 if(value==null) | |
| 306 _setter.invoke(obj, NULL_ARG); | |
| 307 else | |
| 308 invokeObject(obj, value); | |
| 309 } | |
| 310 | |
| 311 protected void invokeObject(Object obj, Object value) throws IllegalArgumentException, | |
| 312 IllegalAccessException, InvocationTargetException | |
| 313 { | |
| 314 | |
| 315 if (_type.isEnum()) | |
| 316 { | |
| 317 if (value instanceof Enum) | |
| 318 _setter.invoke(obj, new Object[]{value}); | |
| 319 else | |
| 320 _setter.invoke(obj, new Object[]{Enum.valueOf((Class<? extends Enum>)_type,value.toString())}); | |
| 321 } | |
| 322 else if(_numberType!=null && value instanceof Number) | |
| 323 { | |
| 324 _setter.invoke(obj, new Object[]{_numberType.getActualValue((Number)value)}); | |
| 325 } | |
| 326 else if (Character.TYPE.equals(_type) || Character.class.equals(_type)) | |
| 327 { | |
| 328 _setter.invoke(obj, new Object[]{String.valueOf(value).charAt(0)}); | |
| 329 } | |
| 330 else if(_componentType!=null && value.getClass().isArray()) | |
| 331 { | |
| 332 if(_numberType==null) | |
| 333 { | |
| 334 int len = Array.getLength(value); | |
| 335 Object array = Array.newInstance(_componentType, len); | |
| 336 try | |
| 337 { | |
| 338 System.arraycopy(value, 0, array, 0, len); | |
| 339 } | |
| 340 catch(Exception e) | |
| 341 { | |
| 342 // unusual array with multiple types | |
| 343 LOG.ignore(e); | |
| 344 _setter.invoke(obj, new Object[]{value}); | |
| 345 return; | |
| 346 } | |
| 347 _setter.invoke(obj, new Object[]{array}); | |
| 348 } | |
| 349 else | |
| 350 { | |
| 351 Object[] old = (Object[])value; | |
| 352 Object array = Array.newInstance(_componentType, old.length); | |
| 353 try | |
| 354 { | |
| 355 for(int i=0; i<old.length; i++) | |
| 356 Array.set(array, i, _numberType.getActualValue((Number)old[i])); | |
| 357 } | |
| 358 catch(Exception e) | |
| 359 { | |
| 360 // unusual array with multiple types | |
| 361 LOG.ignore(e); | |
| 362 _setter.invoke(obj, new Object[]{value}); | |
| 363 return; | |
| 364 } | |
| 365 _setter.invoke(obj, new Object[]{array}); | |
| 366 } | |
| 367 } | |
| 368 else | |
| 369 _setter.invoke(obj, new Object[]{value}); | |
| 370 } | |
| 371 } | |
| 372 | |
| 373 public interface NumberType | |
| 374 { | |
| 375 public Object getActualValue(Number number); | |
| 376 } | |
| 377 | |
| 378 public static final NumberType SHORT = new NumberType() | |
| 379 { | |
| 380 public Object getActualValue(Number number) | |
| 381 { | |
| 382 return new Short(number.shortValue()); | |
| 383 } | |
| 384 }; | |
| 385 | |
| 386 public static final NumberType INTEGER = new NumberType() | |
| 387 { | |
| 388 public Object getActualValue(Number number) | |
| 389 { | |
| 390 return new Integer(number.intValue()); | |
| 391 } | |
| 392 }; | |
| 393 | |
| 394 public static final NumberType FLOAT = new NumberType() | |
| 395 { | |
| 396 public Object getActualValue(Number number) | |
| 397 { | |
| 398 return new Float(number.floatValue()); | |
| 399 } | |
| 400 }; | |
| 401 | |
| 402 public static final NumberType LONG = new NumberType() | |
| 403 { | |
| 404 public Object getActualValue(Number number) | |
| 405 { | |
| 406 return number instanceof Long ? number : new Long(number.longValue()); | |
| 407 } | |
| 408 }; | |
| 409 | |
| 410 public static final NumberType DOUBLE = new NumberType() | |
| 411 { | |
| 412 public Object getActualValue(Number number) | |
| 413 { | |
| 414 return number instanceof Double ? number : new Double(number.doubleValue()); | |
| 415 } | |
| 416 }; | |
| 417 | |
| 418 static | |
| 419 { | |
| 420 __numberTypes.put(Short.class, SHORT); | |
| 421 __numberTypes.put(Short.TYPE, SHORT); | |
| 422 __numberTypes.put(Integer.class, INTEGER); | |
| 423 __numberTypes.put(Integer.TYPE, INTEGER); | |
| 424 __numberTypes.put(Long.class, LONG); | |
| 425 __numberTypes.put(Long.TYPE, LONG); | |
| 426 __numberTypes.put(Float.class, FLOAT); | |
| 427 __numberTypes.put(Float.TYPE, FLOAT); | |
| 428 __numberTypes.put(Double.class, DOUBLE); | |
| 429 __numberTypes.put(Double.TYPE, DOUBLE); | |
| 430 } | |
| 431 } |
