@Override
  public <T> BeanDescriptor<T> provide(Class<T> ofClass, Type ofType, Genson genson) {
    Map<String, LinkedList<PropertyMutator>> mutatorsMap =
        new LinkedHashMap<String, LinkedList<PropertyMutator>>();
    Map<String, LinkedList<PropertyAccessor>> accessorsMap =
        new LinkedHashMap<String, LinkedList<PropertyAccessor>>();

    List<BeanCreator> creators = provideBeanCreators(ofType, genson);

    provideBeanPropertyAccessors(ofType, accessorsMap, genson);
    provideBeanPropertyMutators(ofType, mutatorsMap, genson);

    List<PropertyAccessor> accessors = new ArrayList<PropertyAccessor>(accessorsMap.size());
    for (Map.Entry<String, LinkedList<PropertyAccessor>> entry : accessorsMap.entrySet()) {
      PropertyAccessor accessor = checkAndMergeAccessors(entry.getKey(), entry.getValue());
      // in case of...
      if (accessor != null) accessors.add(accessor);
    }

    Map<String, PropertyMutator> mutators =
        new HashMap<String, PropertyMutator>(mutatorsMap.size());
    for (Map.Entry<String, LinkedList<PropertyMutator>> entry : mutatorsMap.entrySet()) {
      PropertyMutator mutator = checkAndMergeMutators(entry.getKey(), entry.getValue());
      if (mutator != null) mutators.put(mutator.name, mutator);
    }

    BeanCreator ctr = checkAndMerge(ofType, creators);
    if (ctr != null) mergeAccessorsWithCreatorProperties(ofType, accessors, ctr);
    if (ctr != null) mergeMutatorsWithCreatorProperties(ofType, mutators, ctr);

    // 1 - prepare the converters for the accessors
    for (PropertyAccessor accessor : accessors) {
      accessor.propertySerializer = provide(accessor, genson);
    }

    // 2 - prepare the mutators
    for (PropertyMutator mutator : mutators.values()) {
      mutator.propertyDeserializer = provide(mutator, genson);
    }

    // 3 - prepare the converters for creator parameters
    if (ctr != null) {
      for (PropertyMutator mutator : ctr.parameters.values()) {
        mutator.propertyDeserializer = provide(mutator, genson);
      }
    }

    // lets fail fast if the BeanDescriptor has been built for the wrong type.
    // another option could be to pass in all the methods an additional parameter Class<T> that
    // would not necessarily correspond to the rawClass of ofType. In fact we authorize that
    // ofType rawClass is different from Class<T>, but the BeanDescriptor must match!
    BeanDescriptor<T> descriptor = create(ofClass, ofType, ctr, accessors, mutators, genson);
    if (!ofClass.isAssignableFrom(descriptor.getOfClass()))
      throw new ClassCastException(
          "Actual implementation of BeanDescriptorProvider "
              + getClass()
              + " seems to do something wrong. Expected BeanDescriptor for type "
              + ofClass
              + " but provided BeanDescriptor for type "
              + descriptor.getOfClass());
    return descriptor;
  }