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.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.owlike.genson.Converter;
import com.owlike.genson.Factory;
import com.owlike.genson.Genson;
import com.owlike.genson.ThreadLocalHolder;
import com.owlike.genson.convert.ContextualFactory;

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

/**
 * Abstract implementation of {@link BeanDescriptorProvider} applying the template pattern.
 * <p/>
 * If you wonder how to implement the different abstract methods defined in this class have a look
 * at the <a href=
 * "http://code.google.com/p/genson/source/browse/src/main/java/com/owlike/genson/reflect/BaseBeanDescriptorProvider.java"
 * >BaseBeanDescriptorProvider</a>.
 *
 * @author eugen
 */

public abstract class AbstractBeanDescriptorProvider implements BeanDescriptorProvider {
  final static 1.5.0/docs/api/java/lang/String.html">String CONTEXT_KEY = "__GENSON$CREATION_CONTEXT";
  final static 1.5.0/docs/api/java/lang/String.html">String DO_NOT_CACHE_CONVERTER_KEY = "__GENSON$DO_NOT_CACHE_CONVERTER";

  public final static class ContextualConverterFactory {
    private final List<? extends ContextualFactory<?>> contextualFactories;

    public ContextualConverterFactory(List<? extends ContextualFactory<?>> contextualFactories) {
      this.contextualFactories = contextualFactories != null ? new ArrayList<ContextualFactory<?>>(
        contextualFactories) : new ArrayList<ContextualFactory<?>>();
    }

    Converter<?> provide(BeanProperty property, Genson genson) {
      1.5.0/docs/api/java/lang/reflect/Type.html">Type type = property.getType();
      for (Iterator<? extends ContextualFactory<?>> it = contextualFactories.iterator(); it
        .hasNext(); ) {
        ContextualFactory<?> factory = it.next();
        Converter<?> object = null;
        1.5.0/docs/api/java/lang/reflect/Type.html">Type factoryType = lookupGenericType(ContextualFactory.class, factory.getClass());
        factoryType = expandType(factoryType, factory.getClass());
        1.5.0/docs/api/java/lang/reflect/Type.html">Type factoryParameter = typeOf(0, factoryType);

        if (type instanceof Class<?> && ((Class<?>) type).isPrimitive())
          type = wrap((Class<?>) type);

        if (match(type, factoryParameter, false)
          && (object = factory.create(property, genson)) != null) {
          return object;
        }
      }
      return null;
    }
  }

  public final static class ContextualFactoryDecorator implements Factory<Converter<?>> {
    private final Factory<Converter<?>> delegatedFactory;

    public ContextualFactoryDecorator(Factory<Converter<?>> delegatedFactory) {
      this.delegatedFactory = delegatedFactory;
    }

    @1.5.0/docs/api/java/lang/Override.html">Override
    public Converter<?> create(1.5.0/docs/api/java/lang/reflect/Type.html">Type type, Genson genson) {
      Converter<?> converter = ThreadLocalHolder.get(CONTEXT_KEY, Converter.class);
      if (converter != null) return converter;
      return delegatedFactory.create(type, genson);
    }
  }

  private final ContextualConverterFactory contextualConverterFactory;

  protected AbstractBeanDescriptorProvider(ContextualConverterFactory contextualConverterFactory) {
    this.contextualConverterFactory = contextualConverterFactory;
  }

  @1.5.0/docs/api/java/lang/Override.html">Override
  public <T> BeanDescriptor<T> provide(Class<T> type, Genson genson) {
    return provide(type, type, genson);
  }

  @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) {
    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 (1.5.0/docs/api/java/util/Map.html">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 (1.5.0/docs/api/java/util/Map.html">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);
      }
    }

    for (PropertyMutator p : mutators.values()) {
      for (1.5.0/docs/api/java/lang/String.html">String alias : p.aliases()) mutators.put(alias, p);
    }

    // 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 1.5.0/docs/api/java/lang/ClassCastException.html">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;
  }

  private Converter<Object> provide(BeanProperty property, Genson genson) {
    // contextual converters must not be retrieved from cache nor stored in cache, by first
    // trying to create it and reusing it during the
    // call to genson.provideConverter we avoid retrieving it from cache, and by setting
    // DO_NOT_CACHE_CONVERTER to true we tell genson not to store
    // this converter in cache

    @1.5.0/docs/api/java/lang/SuppressWarnings.html">SuppressWarnings("unchecked")
    Converter<Object> converter = (Converter<Object>) contextualConverterFactory.provide(
      property, genson);
    if (converter != null) {
      ThreadLocalHolder.store(DO_NOT_CACHE_CONVERTER_KEY, true);
      ThreadLocalHolder.store(CONTEXT_KEY, converter);
    }
    try {
      return genson.provideConverter(property.type);
    } finally {
      if (converter != null) {
        ThreadLocalHolder.remove(DO_NOT_CACHE_CONVERTER_KEY, 1.5.0/docs/api/java/lang/Boolean.html">Boolean.class);
        ThreadLocalHolder.remove(CONTEXT_KEY, Converter.class);
      }
    }
  }

  /**
   * Creates an instance of BeanDescriptor based on the passed arguments. Subclasses can override
   * this method to create their own BeanDescriptors.
   *
   * @param forClass
   * @param ofType
   * @param creator
   * @param accessors
   * @param mutators
   * @return a instance
   */

  protected <T> BeanDescriptor<T> create(Class<T> forClass, 1.5.0/docs/api/java/lang/reflect/Type.html">Type ofType, BeanCreator creator,
                                         List<PropertyAccessor> accessors, Map<String, PropertyMutator> mutators,
                                         Genson genson) {
    return new BeanDescriptor<T>(forClass, getRawClass(ofType), accessors, mutators, creator, genson.failOnMissingProperty());
  }

  /**
   * Provides a list of {@link BeanCreator} for type ofType.
   *
   * @param ofType
   * @param genson
   * @return a list of resolved bean creators
   */

  protected abstract List<BeanCreator> provideBeanCreators(1.5.0/docs/api/java/lang/reflect/Type.html">Type ofType, Genson genson);

  /**
   * Adds resolved {@link PropertyMutator} to mutatorsMap.
   *
   * @param ofType
   * @param mutatorsMap
   * @param genson
   */

  protected abstract void provideBeanPropertyMutators(1.5.0/docs/api/java/lang/reflect/Type.html">Type ofType,
                                                      Map<String, LinkedList<PropertyMutator>> mutatorsMap, Genson genson);

  /**
   * Adds resolved {@link PropertyAccessor} to accessorsMap.
   *
   * @param ofType
   * @param accessorsMap
   * @param genson
   */

  protected abstract void provideBeanPropertyAccessors(1.5.0/docs/api/java/lang/reflect/Type.html">Type ofType,
                                                       Map<String, LinkedList<PropertyAccessor>> accessorsMap, Genson genson);

  /**
   * Implementations of this method can do some additional checks on the creators validity or do
   * any other operations related to creators. This method must merge all creators into a single
   * one.
   *
   * @param creators
   * @return the creator that will be used by the BeanDescriptor
   */

  protected abstract BeanCreator checkAndMerge(1.5.0/docs/api/java/lang/reflect/Type.html">Type ofType, List<BeanCreator> creators);

  /**
   * Implementations are supposed to merge the {@link PropertyMutator}s from mutators list into a
   * single PropertyMutator.
   *
   * @param name
   * @param mutators
   * @return a single PropertyMutator or null.
   */

  protected abstract PropertyMutator checkAndMergeMutators(1.5.0/docs/api/java/lang/String.html">String name,
                                                           LinkedList<PropertyMutator> mutators);

  /**
   * Implementations may do additional merge operations based on the resolved creator
   * parameters and the resolved mutators.
   *
   * @param ofType
   * @param mutators
   * @param creator
   */

  protected abstract void mergeMutatorsWithCreatorProperties(1.5.0/docs/api/java/lang/reflect/Type.html">Type ofType, Map<String, PropertyMutator> mutators, BeanCreator creator);

  /**
   * Implementations may do additional merge operations based on the resolved creator
   * parameters and the resolved accessors.
   *
   * @param ofType
   * @param accessors
   * @param creator
   */

  protected abstract void mergeAccessorsWithCreatorProperties(1.5.0/docs/api/java/lang/reflect/Type.html">Type ofType, List<PropertyAccessor> accessors, BeanCreator creator);

  /**
   * Implementations are supposed to merge the {@link PropertyAccessor}s from accessors list into
   * a single PropertyAccessor.
   *
   * @param name
   * @param accessors
   * @return a single property accessor for this name
   */

  protected abstract PropertyAccessor checkAndMergeAccessors(1.5.0/docs/api/java/lang/String.html">String name,
                                                             LinkedList<PropertyAccessor> accessors);
}