Subversion Repositories bacoAlunos

Rev

Rev 1969 | Blame | Compare with Previous | Last modification | View Log | RSS feed

package com.owlike.genson.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static com.owlike.genson.Trilean.FALSE;
import static com.owlike.genson.Trilean.TRUE;
import static com.owlike.genson.reflect.TypeUtil.*;

import com.owlike.genson.*;
import com.owlike.genson.annotation.JsonCreator;
import com.owlike.genson.reflect.PropertyAccessor.MethodAccessor;
import com.owlike.genson.reflect.PropertyMutator.MethodMutator;

/**
 * This class constructs BeanDescriptors for the {@link com.owlike.genson.BeanView BeanView}
 * mechanism. This class is mainly intended for internal use. It can be directly used if needed to
 * get a BeanDescriptor instance for a BeanView (for example if you want to deserialize into an
 * existing object and apply a BeanView). Extending BeanViewDescriptorProvider should be avoided.
 *
 * @author eugen
 */

public class BeanViewDescriptorProvider extends BaseBeanDescriptorProvider {

  private Map<Class<?>, BeanView<?>> views;
  private Map<Class<?>, BeanDescriptor<?>> descriptors = new ConcurrentHashMap<Class<?>, BeanDescriptor<?>>();

  public BeanViewDescriptorProvider(ContextualConverterFactory ctxConverterFactory,
                                    Map<Class<?>, BeanView<?>> views, BeanPropertyFactory propertyFactory,
                                    BeanMutatorAccessorResolver mutatorAccessorResolver,
                                    PropertyNameResolver nameResolver) {
    super(ctxConverterFactory, propertyFactory, mutatorAccessorResolver, nameResolver, true, false, true);
    this.views = views;
  }

  @1.5.0/docs/api/java/lang/SuppressWarnings.html">SuppressWarnings("unchecked")
  @1.5.0/docs/api/java/lang/Override.html">Override
  public <T> BeanDescriptor<T> provide(Class<T> ofClass,
                                       1.5.0/docs/api/java/lang/reflect/Type.html">Type ofType, Genson genson) {
    Class<?> rawClass = getRawClass(ofType);
    if (!BeanView.class.isAssignableFrom(rawClass))
      throw new 1.5.0/docs/api/java/lang/IllegalArgumentException.html">IllegalArgumentException("Expected argument of type "
        + BeanView.class.getName() + " but provided " + rawClass);

    BeanDescriptor<T> descriptor = (BeanDescriptor<T>) descriptors.get(rawClass);
    if (descriptor == null) {
      Class<?> parameterizedTypeForBeanView =
        getRawClass(expandType(BeanView.class.getTypeParameters()[0], ofType));
      if (!ofClass.isAssignableFrom(parameterizedTypeForBeanView)) {
        throw new 1.5.0/docs/api/java/lang/IllegalArgumentException.html">IllegalArgumentException(
          "Expected type for ofClass parameter is " + parameterizedTypeForBeanView
            + " but provided is " + ofClass);
      }

      try {
        if (!views.containsKey(rawClass)) {
          Constructor<BeanView<T>> ctr =
            (Constructor<BeanView<T>>) rawClass.getDeclaredConstructor();
          if (!ctr.isAccessible()) ctr.setAccessible(true);
          views.put(rawClass, ctr.newInstance());
        }
        descriptor = super.provide(ofClass, ofType, genson);
        descriptors.put(rawClass, descriptor);
      } catch (1.5.0/docs/api/java/lang/SecurityException.html">SecurityException e) {
        throw couldNotInstantiateBeanView(ofClass, e);
      } catch (1.5.0/docs/api/java/lang/NoSuchMethodException.html">NoSuchMethodException e) {
        throw couldNotInstantiateBeanView(ofClass, e);
      } catch (1.5.0/docs/api/java/lang/IllegalArgumentException.html">IllegalArgumentException e) {
        throw couldNotInstantiateBeanView(ofClass, e);
      } catch (1.5.0/docs/api/java/lang/InstantiationException.html">InstantiationException e) {
        throw couldNotInstantiateBeanView(ofClass, e);
      } catch (1.5.0/docs/api/java/lang/IllegalAccessException.html">IllegalAccessException e) {
        throw couldNotInstantiateBeanView(ofClass, e);
      } catch (1.5.0/docs/api/java/lang/reflect/InvocationTargetException.html">InvocationTargetException e) {
        throw couldNotInstantiateBeanView(ofClass, e);
      }
    }
    return descriptor;
  }

  private JsonBindingException couldNotInstantiateBeanView(Class<?> beanViewClass,
                                                           1.5.0/docs/api/java/lang/Exception.html">Exception e) {
    return new JsonBindingException("Could not instantiate BeanView "
      + beanViewClass.getName()
      + ", BeanView implementations must have a public no arg constructor.", e);
  }

  @1.5.0/docs/api/java/lang/Override.html">Override
  public List<BeanCreator> provideBeanCreators(1.5.0/docs/api/java/lang/reflect/Type.html">Type ofType, Genson genson) {
    List<BeanCreator> creators = new ArrayList<BeanCreator>();
    for (Class<?> clazz = getRawClass(ofType); clazz != null && !5+0%2Fdocs%2Fapi+Object">Object.class.equals(clazz); clazz =
      clazz.getSuperclass()) {
      provideMethodCreators(clazz, creators, ofType, genson);
    }
    1.5.0/docs/api/java/lang/reflect/Type.html">Type viewForType = TypeUtil.expandType(BeanView.class.getTypeParameters()[0], ofType);
    List<BeanCreator> oCtrs = super.provideBeanCreators(viewForType, genson);
    creators.addAll(oCtrs);
    return creators;
  }

  public static class BeanViewPropertyFactory implements BeanPropertyFactory {
    private final Map<Class<?>, BeanView<?>> views;

    public BeanViewPropertyFactory(Map<Class<?>, BeanView<?>> views) {
      this.views = views;
    }

    public PropertyAccessor createAccessor(1.5.0/docs/api/java/lang/String.html">String name, 1.5.0/docs/api/java/lang/reflect/Method.html">Method method, 1.5.0/docs/api/java/lang/reflect/Type.html">Type ofType,
                                           Genson genson) {
      // the target bean must be first (and single) parameter for beanview accessors
      BeanView<?> beanview = views.get(getRawClass(ofType));
      if (beanview != null) {
        1.5.0/docs/api/java/lang/reflect/Type.html">Type superTypeWithParameter =
          TypeUtil.lookupGenericType(BeanView.class, beanview.getClass());
        Class<?> tClass =
          getRawClass(typeOf(0,
            expandType(superTypeWithParameter, beanview.getClass())));
        1.5.0/docs/api/java/lang/reflect/Type.html">Type type = expandType(method.getGenericReturnType(), ofType);
        return new BeanViewPropertyAccessor(name, method, type, beanview, tClass);
      } else return null;
    }

    public PropertyMutator createMutator(1.5.0/docs/api/java/lang/String.html">String name, 1.5.0/docs/api/java/lang/reflect/Method.html">Method method, 1.5.0/docs/api/java/lang/reflect/Type.html">Type ofType, Genson genson) {
      // the target bean must be second parameter for beanview mutators
      BeanView<?> beanview = views.get(getRawClass(ofType));
      if (beanview != null) {
        1.5.0/docs/api/java/lang/reflect/Type.html">Type superTypeWithParameter =
          TypeUtil.lookupGenericType(BeanView.class, beanview.getClass());
        Class<?> tClass =
          getRawClass(typeOf(0,
            expandType(superTypeWithParameter, beanview.getClass())));
        1.5.0/docs/api/java/lang/reflect/Type.html">Type type = expandType(method.getGenericParameterTypes()[0], ofType);
        return new BeanViewPropertyMutator(name, method, type, beanview, tClass);
      } else return null;
    }

    @1.5.0/docs/api/java/lang/Override.html">Override
    public PropertyAccessor createAccessor(1.5.0/docs/api/java/lang/String.html">String name, 1.5.0/docs/api/java/lang/reflect/Field.html">Field field, 1.5.0/docs/api/java/lang/reflect/Type.html">Type ofType,
                                           Genson genson) {
      return null;
    }

    @1.5.0/docs/api/java/lang/Override.html">Override
    public BeanCreator createCreator(1.5.0/docs/api/java/lang/reflect/Type.html">Type ofType, Constructor<?> ctr,
                                     1.5.0/docs/api/java/lang/String.html">String[] resolvedNames, Genson genson) {
      return null;
    }

    @1.5.0/docs/api/java/lang/Override.html">Override
    public BeanCreator createCreator(1.5.0/docs/api/java/lang/reflect/Type.html">Type ofType, 1.5.0/docs/api/java/lang/reflect/Method.html">Method method,
                                     1.5.0/docs/api/java/lang/String.html">String[] resolvedNames, Genson genson) {
      return null;
    }

    @1.5.0/docs/api/java/lang/Override.html">Override
    public PropertyMutator createMutator(1.5.0/docs/api/java/lang/String.html">String name, 1.5.0/docs/api/java/lang/reflect/Field.html">Field field, 1.5.0/docs/api/java/lang/reflect/Type.html">Type ofType,
                                         Genson genson) {
      return null;
    }
  }

  public static class BeanViewMutatorAccessorResolver implements BeanMutatorAccessorResolver {

    public Trilean isAccessor(1.5.0/docs/api/java/lang/reflect/Field.html">Field field, Class<?> baseClass) {
      return FALSE;
    }

    public Trilean isAccessor(1.5.0/docs/api/java/lang/reflect/Method.html">Method method, Class<?> baseClass) {
      1.5.0/docs/api/java/lang/reflect/Type.html">Type expectedType = TypeUtil.lookupGenericType(BeanView.class, baseClass);
      expectedType = TypeUtil.expandType(expectedType, baseClass);
      expectedType = TypeUtil.typeOf(0, expectedType);
      int modifiers = method.getModifiers();
      return Trilean.valueOf((method.getName().startsWith("get") || (method.getName()
        .startsWith("is") && (TypeUtil.match(method.getGenericReturnType(),
        1.5.0/docs/api/java/lang/Boolean.html">Boolean.class, false) || boolean.class.equals(method.getReturnType()))))
        && TypeUtil.match(expectedType, method.getGenericParameterTypes()[0], false)
        && 1.5.0/docs/api/java/lang/reflect/Modifier.html">Modifier.isPublic(modifiers)
        && !1.5.0/docs/api/java/lang/reflect/Modifier.html">Modifier.isAbstract(modifiers)
        && !1.5.0/docs/api/java/lang/reflect/Modifier.html">Modifier.isNative(modifiers));
    }

    public Trilean isCreator(Constructor<?> constructor, Class<?> baseClass) {
      int modifier = constructor.getModifiers();
      return Trilean.valueOf(1.5.0/docs/api/java/lang/reflect/Modifier.html">Modifier.isPublic(modifier)
        || !(1.5.0/docs/api/java/lang/reflect/Modifier.html">Modifier.isPrivate(modifier) || 1.5.0/docs/api/java/lang/reflect/Modifier.html">Modifier.isProtected(modifier)));
    }

    public Trilean isCreator(1.5.0/docs/api/java/lang/reflect/Method.html">Method method, Class<?> baseClass) {
      if (method.getAnnotation(JsonCreator.class) != null) {
        if (1.5.0/docs/api/java/lang/reflect/Modifier.html">Modifier.isStatic(method.getModifiers())) return TRUE;
        throw new JsonBindingException("Method " + method.toGenericString()
          + " annotated with @Creator must be static!");
      }
      return FALSE;
    }

    public Trilean isMutator(1.5.0/docs/api/java/lang/reflect/Field.html">Field field, Class<?> baseClass) {
      return FALSE;
    }

    public Trilean isMutator(1.5.0/docs/api/java/lang/reflect/Method.html">Method method, Class<?> baseClass) {
      1.5.0/docs/api/java/lang/reflect/Type.html">Type expectedType = TypeUtil.lookupGenericType(BeanView.class, baseClass);
      expectedType = TypeUtil.expandType(expectedType, baseClass);
      expectedType = TypeUtil.typeOf(0, expectedType);
      int modifiers = method.getModifiers();
      return Trilean.valueOf(method.getName().startsWith("set")
        && void.class.equals(method.getReturnType())
        && method.getGenericParameterTypes().length == 2
        && TypeUtil.match(expectedType, method.getGenericParameterTypes()[1], false)
        && 1.5.0/docs/api/java/lang/reflect/Modifier.html">Modifier.isPublic(modifiers) && !1.5.0/docs/api/java/lang/reflect/Modifier.html">Modifier.isAbstract(modifiers)
        && !1.5.0/docs/api/java/lang/reflect/Modifier.html">Modifier.isNative(modifiers));
    }

  }

  private static class BeanViewPropertyAccessor extends MethodAccessor {
    private final BeanView<?> _view;

    public BeanViewPropertyAccessor(1.5.0/docs/api/java/lang/String.html">String name, 1.5.0/docs/api/java/lang/reflect/Method.html">Method getter, 1.5.0/docs/api/java/lang/reflect/Type.html">Type type, BeanView<?> target,
                                    Class<?> tClass) {
      super(name, getter, type, tClass);
      this._view = target;
    }

    @1.5.0/docs/api/java/lang/Override.html">Override
    public 5+0%2Fdocs%2Fapi+Object">Object access(5+0%2Fdocs%2Fapi+Object">Object target) {
      try {
        return _getter.invoke(_view, target);
      } catch (1.5.0/docs/api/java/lang/IllegalArgumentException.html">IllegalArgumentException e) {
        throw couldNotAccess(e);
      } catch (1.5.0/docs/api/java/lang/IllegalAccessException.html">IllegalAccessException e) {
        throw couldNotAccess(e);
      } catch (1.5.0/docs/api/java/lang/reflect/InvocationTargetException.html">InvocationTargetException e) {
        throw couldNotAccess(e);
      }
    }
  }

  private static class BeanViewPropertyMutator extends MethodMutator {
    private final BeanView<?> _view;

    public BeanViewPropertyMutator(1.5.0/docs/api/java/lang/String.html">String name, 1.5.0/docs/api/java/lang/reflect/Method.html">Method setter, 1.5.0/docs/api/java/lang/reflect/Type.html">Type type, BeanView<?> target,
                                   Class<?> tClass) {
      super(name, setter, type, tClass);
      this._view = target;
    }

    @1.5.0/docs/api/java/lang/Override.html">Override
    public void mutate(5+0%2Fdocs%2Fapi+Object">Object target, 5+0%2Fdocs%2Fapi+Object">Object value) {
      try {
        _setter.invoke(_view, value, target);
      } catch (1.5.0/docs/api/java/lang/IllegalArgumentException.html">IllegalArgumentException e) {
        throw couldNotMutate(e);
      } catch (1.5.0/docs/api/java/lang/IllegalAccessException.html">IllegalAccessException e) {
        throw couldNotMutate(e);
      } catch (1.5.0/docs/api/java/lang/reflect/InvocationTargetException.html">InvocationTargetException e) {
        throw couldNotMutate(e);
      }
    }
  }
}