All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.owlike.genson.reflect.BaseBeanDescriptorProvider Maven / Gradle / Ivy

The newest version!
package com.owlike.genson.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.owlike.genson.Genson;
import com.owlike.genson.annotation.JsonCreator;
import com.owlike.genson.reflect.BeanCreator.BeanCreatorProperty;

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

/**
 * Standard implementation of AbstractBeanDescriptorProvider that uses
 * {@link BeanMutatorAccessorResolver} and {@link PropertyNameResolver}. If you want to change the
 * way BeanDescriptors are created you can subclass this class and override the needed methods. If
 * you only want to create instances of your own PropertyMutators/PropertyAccessors or BeanCreators
 * just override the corresponding createXXX methods.
 *
 * @author eugen
 */
public class BaseBeanDescriptorProvider extends AbstractBeanDescriptorProvider {

  private final static Comparator _beanCreatorsComparator = new Comparator() {
    public int compare(BeanCreator o1, BeanCreator o2) {
      return o1.parameters.size() - o2.parameters.size();
    }
  };

  private final BeanPropertyFactory propertyFactory;
  protected final BeanMutatorAccessorResolver mutatorAccessorResolver;
  protected final PropertyNameResolver nameResolver;
  protected final boolean useGettersAndSetters;
  protected final boolean useFields;
  protected final boolean favorEmptyCreators;

  public BaseBeanDescriptorProvider(ContextualConverterFactory ctxConverterFactory, BeanPropertyFactory propertyFactory,
                                    BeanMutatorAccessorResolver mutatorAccessorResolver, PropertyNameResolver nameResolver,
                                    boolean useGettersAndSetters, boolean useFields, boolean favorEmptyCreators) {
    super(ctxConverterFactory);

    if (mutatorAccessorResolver == null)
      throw new IllegalArgumentException("mutatorAccessorResolver must be not null!");
    if (nameResolver == null)
      throw new IllegalArgumentException("nameResolver must be not null!");
    if (propertyFactory == null)
      throw new IllegalArgumentException("propertyFactory must be not null!");

    this.propertyFactory = propertyFactory;
    this.mutatorAccessorResolver = mutatorAccessorResolver;
    this.nameResolver = nameResolver;
    this.useFields = useFields;
    this.useGettersAndSetters = useGettersAndSetters;
    if (!useFields && !useGettersAndSetters)
      throw new IllegalArgumentException("You must allow at least one mode: with fields or methods.");
    this.favorEmptyCreators = favorEmptyCreators;
  }

  @Override
  public List provideBeanCreators(Type ofType, Genson genson) {
    List creators = new ArrayList();
    Class ofClass = getRawClass(ofType);
    if (ofClass.isMemberClass() && (ofClass.getModifiers() & Modifier.STATIC) == 0)
      return creators;

    provideConstructorCreators(ofType, creators, genson);
    for (Class clazz = ofClass; clazz != null && !Object.class.equals(clazz); clazz = clazz
      .getSuperclass()) {
      provideMethodCreators(clazz, creators, ofType, genson);
    }
    return creators;
  }

  @Override
  public void provideBeanPropertyAccessors(Type ofType,
                                           Map> accessorsMap, Genson genson) {
    ArrayDeque> classesToInspect = new ArrayDeque>();
    classesToInspect.push(getRawClass(ofType));
    while (!classesToInspect.isEmpty()) {
      Class clazz = classesToInspect.pop();
      if (clazz.getSuperclass() != null && clazz.getSuperclass() != Object.class) {
        classesToInspect.push(clazz.getSuperclass());
      }
      for (Class anInterface : clazz.getInterfaces()) classesToInspect.push(anInterface);

      // first lookup for fields
      if (useFields) provideFieldAccessors(clazz, accessorsMap, ofType, genson);
      // and now search methods (getters)
      if (useGettersAndSetters) provideMethodAccessors(clazz, accessorsMap, ofType, genson);
    }
  }

  @Override
  public void provideBeanPropertyMutators(Type ofType,
                                          Map> mutatorsMap, Genson genson) {
    ArrayDeque> classesToInspect = new ArrayDeque>();
    classesToInspect.push(getRawClass(ofType));
    while (!classesToInspect.isEmpty()) {
      Class clazz = classesToInspect.pop();
      if (clazz.getSuperclass() != null && clazz.getSuperclass() != Object.class) {
        classesToInspect.push(clazz.getSuperclass());
      }
      for (Class anInterface : clazz.getInterfaces()) classesToInspect.push(anInterface);

      // first lookup for fields
      if (useFields) provideFieldMutators(clazz, mutatorsMap, ofType, genson);
      // and now search methods (getters)
      if (useGettersAndSetters) provideMethodMutators(clazz, mutatorsMap, ofType, genson);
    }
  }

  protected void provideConstructorCreators(Type ofType, List creators, Genson genson) {
    Class ofClass = getRawClass(ofType);
    Constructor[] ctrs = ofClass.getDeclaredConstructors();
    for (Constructor ctr : ctrs) {
      if (TRUE == mutatorAccessorResolver.isCreator(ctr, ofClass)) {
        Type[] parameterTypes = ctr.getGenericParameterTypes();
        int paramCnt = parameterTypes.length;
        String[] parameterNames = new String[paramCnt];
        int idx = 0;
        for (; idx < paramCnt; idx++) {
          String name = nameResolver.resolve(idx, ctr);
          if (name == null) break;
          parameterNames[idx] = name;
        }

        if (idx == paramCnt) {
          BeanCreator creator = propertyFactory.createCreator(ofType, ctr, parameterNames, genson);
          creators.add(creator);
        }
      }
    }
  }

  protected void provideMethodCreators(Class ofClass, List creators, Type ofType,
                                       Genson genson) {
    Method[] ctrs = ofClass.getDeclaredMethods();
    for (Method ctr : ctrs) {
      if (TRUE == mutatorAccessorResolver.isCreator(ctr, getRawClass(ofType))) {
        Type[] parameterTypes = ctr.getGenericParameterTypes();
        int paramCnt = parameterTypes.length;
        String[] parameterNames = new String[paramCnt];
        int idx = 0;
        for (; idx < paramCnt; idx++) {
          String name = nameResolver.resolve(idx, ctr);
          if (name == null) break;
          parameterNames[idx] = name;
        }

        if (idx == paramCnt) {
          BeanCreator creator = propertyFactory.createCreator(ofType, ctr, parameterNames, genson);
          creators.add(creator);
        }
      }
    }
  }

  protected void provideFieldAccessors(Class ofClass,
                                       Map> accessorsMap, Type ofType, Genson genson) {
    Field[] fields = ofClass.getDeclaredFields();
    for (Field field : fields) {
      if (TRUE == mutatorAccessorResolver.isAccessor(field, getRawClass(ofType))) {
        String name = nameResolver.resolve(field);
        if (name == null) {
          throw new IllegalStateException("Field '" + field.getName() + "' from class "
            + ofClass.getName()
            + " has been discovered as accessor but its name couldn't be resolved!");
        }
        PropertyAccessor accessor = propertyFactory.createAccessor(name, field, ofType, genson);
        update(accessor, accessorsMap);
      }
    }
  }

  protected void provideMethodAccessors(Class ofClass,
                                        Map> accessorsMap, Type ofType, Genson genson) {
    Method[] methods = ofClass.getDeclaredMethods();
    for (Method method : methods) {
      if (TRUE == mutatorAccessorResolver.isAccessor(method, getRawClass(ofType))) {
        String name = nameResolver.resolve(method);
        if (name == null) {
          throw new IllegalStateException("Method '" + method.getName() + "' from class "
            + ofClass.getName()
            + " has been discovered as accessor but its name couldn't be resolved!");
        }
        PropertyAccessor accessor = propertyFactory.createAccessor(name, method, ofType, genson);
        update(accessor, accessorsMap);
      }
    }
  }

  protected void provideFieldMutators(Class ofClass,
                                      Map> mutatorsMap, Type ofType, Genson genson) {
    Field[] fields = ofClass.getDeclaredFields();
    for (Field field : fields) {
      if (TRUE == mutatorAccessorResolver.isMutator(field, getRawClass(ofType))) {
        String name = nameResolver.resolve(field);
        if (name == null) {
          throw new IllegalStateException("Field '" + field.getName() + "' from class "
            + ofClass.getName()
            + " has been discovered as mutator but its name couldn't be resolved!");
        }

        PropertyMutator mutator = propertyFactory.createMutator(name, field, ofType, genson);
        update(mutator, mutatorsMap);
      }
    }
  }

  protected void provideMethodMutators(Class ofClass,
                                       Map> mutatorsMap, Type ofType, Genson genson) {
    Method[] methods = ofClass.getDeclaredMethods();
    for (Method method : methods) {
      if (TRUE == mutatorAccessorResolver.isMutator(method, getRawClass(ofType))) {
        String name = nameResolver.resolve(method);
        if (name == null) {
          throw new IllegalStateException("Method '" + method.getName() + "' from class "
            + ofClass.getName()
            + " has been discovered as mutator but its name couldn't be resolved!");
        }
        PropertyMutator mutator = propertyFactory.createMutator(name, method, ofType, genson);
        update(mutator, mutatorsMap);
      }
    }
  }

  protected  void update(T property, Map> map) {
    LinkedList accessors = map.get(property.name);
    if (accessors == null) {
      accessors = new LinkedList();
      map.put(property.name, accessors);
    }
    accessors.add(property);
  }

  @Override
  protected BeanCreator checkAndMerge(Type ofType, List creators) {
    Class ofClass = getRawClass(ofType);
    // hum maybe do not check this case as we may have class that will only be serialized so
    // they do not need a ctr?
    // if (creators == null || creators.isEmpty())
    // throw new IllegalStateException("Could not create BeanDescriptor for type "
    // + ofClass.getName() + ", no creator has been found.");
    if (creators == null || creators.isEmpty()) return null;

    // now lets do the merge
    if (favorEmptyCreators) {
      Collections.sort(creators, _beanCreatorsComparator);
    }

    boolean hasCreatorAnnotation = false;
    BeanCreator creator = null;

    // first lets do some checks
    for (int i = 0; i < creators.size(); i++) {
      BeanCreator ctr = creators.get(i);
      if (ctr.isAnnotationPresent(JsonCreator.class)) {
        if (!hasCreatorAnnotation)
          hasCreatorAnnotation = true;
        else
          _throwCouldCreateBeanDescriptor(ofClass,
            " only one @JsonCreator annotation per class is allowed.");
      }
    }

    if (hasCreatorAnnotation) {
      for (BeanCreator ctr : creators)
        if (ctr.isAnnotationPresent(JsonCreator.class)) return ctr;
    } else {
      creator = creators.get(0);
    }

    return creator;
  }

  protected void _throwCouldCreateBeanDescriptor(Class ofClass, String reason) {
    throw new IllegalStateException("Could not create BeanDescriptor for type "
      + ofClass.getName() + "," + reason);
  }

  @Override
  protected PropertyAccessor checkAndMergeAccessors(String name,
                                                    LinkedList accessors) {
    PropertyAccessor accessor = _mostSpecificPropertyDeclaringClass(name, accessors);
    return VisibilityFilter.ABSTRACT.isVisible(accessor.getModifiers()) ? accessor : null;
  }

  @Override
  protected PropertyMutator checkAndMergeMutators(String name,
                                                  LinkedList mutators) {
    PropertyMutator mutator = _mostSpecificPropertyDeclaringClass(name, mutators);
    return VisibilityFilter.ABSTRACT.isVisible(mutator.getModifiers()) ? mutator : null;
  }

  protected  T _mostSpecificPropertyDeclaringClass(String name,
                                                                           LinkedList properties) {
    Iterator it = properties.iterator();
    T property = it.next();
    for (; it.hasNext(); ) {
      T next = it.next();
      // Doesn't matter which one will be used, we want to merge the metadata
      next.updateBoth(property);
      // 1 we search the most specialized class containing this property
      // with highest priority
      if ((property.declaringClass.equals(next.declaringClass) && property.priority() < next.priority())
      || property.declaringClass.isAssignableFrom(next.declaringClass)) {
        property = next;
      } else continue;
    }

    return property;
  }

  @Override
  protected void mergeMutatorsWithCreatorProperties(Type ofType, Map mutators, BeanCreator creator) {

    for (Map.Entry entry : creator.parameters.entrySet()) {
      PropertyMutator muta = mutators.get(entry.getKey());
      if (muta == null) {
        // add to mutators only creator properties that don't exist as standard
        // mutator (dont exist as field or method, but only as ctr arg)
        BeanCreatorProperty ctrProperty = entry.getValue();
        mutators.put(entry.getKey(), ctrProperty);
      } else {
        // update the creator property annotations with mutator annotations and vice versa
        entry.getValue().updateBoth(muta);
      }
    }
  }

  @Override
  protected void mergeAccessorsWithCreatorProperties(Type ofType, List accessors, BeanCreator creator) {
    // do nothing
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy