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

com.caucho.v5.convert.ConvertManagerInject Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
/*
 * Copyright (c) 1998-2015 Caucho Technology -- all rights reserved
 *
 * This file is part of Baratine(TM)(TM)
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Baratine is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Baratine is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Baratine; if not, write to the
 * 
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.v5.convert;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import com.caucho.v5.config.Priorities;
import com.caucho.v5.inject.type.TypeRef;
import com.caucho.v5.util.L10N;

import io.baratine.convert.Convert;
import io.baratine.convert.ConvertManager;
import io.baratine.convert.ConvertManagerType;
import io.baratine.inject.Binding;
import io.baratine.inject.Injector;
import io.baratine.inject.Key;

/**
 * Default converter.
 */
public class ConvertManagerInject implements ConvertManager
{
  private static final L10N L = new L10N(ConvertManagerInject.class);
  
  private static ConverterIdentity IDENTITY = new ConverterIdentity<>();
  
  private HashMap,ConvertManagerTypeImpl> _convertMap
    = new HashMap<>();
  
  private Injector _injector;
  
  public ConvertManagerInject(Injector injector)
  {
    _injector = injector;
    
    init();
  }
  
  private void init()
  {
    for (ConvertManagerType convertManager
        : ConvertManagerImpl.convertManagers()) {
      add(convertManager);
    }
    
    for (Binding> binding
        : _injector.bindings(new Key>() {})) {
      add(binding.provider().get());
    }
    
    for (Binding> binding
        : _injector.bindings(new Key>() {})) {
      TypeRef convertRef = TypeRef.of(binding.key().type())
                                  .to(Convert.class);
      Class sourceType = convertRef.param(0).rawClass();
      Class targetType = convertRef.param(1).rawClass();
      
      add(sourceType, targetType, (Convert) binding.provider().get());
    }
  }
  
  private  void add(ConvertManagerType convertManager)
  {
    Class sourceType = convertManager.sourceType();
    
    ConvertManagerTypeImpl typeImpl = getOrCreate(sourceType);
    
    typeImpl.add(convertManager);
  }
  
  private  void add(Class sourceType,
                         Class targetType,
                         Convert converter)
  {
    Objects.requireNonNull(converter);
    
    ConvertManagerTypeImpl typeImpl = getOrCreate(sourceType);
    
    typeImpl.add(targetType, converter);
  }

  /**
   * Returns the converter for a given source type and target type.
   */
  @Override
  public  Convert converter(Class source, Class target)
  {
    ConvertManagerType convertType = getOrCreate(source);
    
    return convertType.converter(target);
  }

  /**
   * Returns the ConvertManagerTypeImpl for a given source type.
   */
  private  ConvertManagerTypeImpl getOrCreate(Class sourceType)
  {
    ConvertManagerTypeImpl convertType
    = (ConvertManagerTypeImpl) _convertMap.get(sourceType);
  
    if (convertType != null) {
      return convertType;
    }
    
    convertType = new ConvertManagerTypeImpl<>(sourceType);
    
    _convertMap.putIfAbsent(sourceType, convertType);

    return (ConvertManagerTypeImpl) _convertMap.get(sourceType);
  }
  
  static final class ConvertManagerTypeImpl implements ConvertManagerType
  {
    private Class _sourceType;
    private ArrayList> _delegates = new ArrayList<>();
    
    private ConcurrentHashMap,Convert> _converterMap
      = new ConcurrentHashMap<>();
    
    ConvertManagerTypeImpl(Class sourceType)
    {
      _sourceType = sourceType;
    }
    
    public Class sourceType()
    {
      return _sourceType;
    }
    
    void add(ConvertManagerType delegate)
    {
      Objects.requireNonNull(delegate);
      
      _delegates.add(delegate);
      Collections.sort(_delegates, (x,y)->Priorities.compare(x,y));
    }
    
     void add(Class target, Convert converter)
    {
      Objects.requireNonNull(converter);
      
      _converterMap.put(target, converter);
    }

    @Override
    public  Convert converter(Class target)
    {
      Convert converter = (Convert) _converterMap.get(target);
      
      if (converter != null) {
        return converter;
      }
      else {
        converter = findConverter(target);
        
        _converterMap.putIfAbsent(target, converter);
        
        return (Convert) _converterMap.get(target);
      }
    }
    
    private  Convert findConverter(Class target)
    {
      for (ConvertManagerType delegate : _delegates) {
        Convert convert = delegate.converter(target);
        
        if (convert != null) {
          return convert;
        }
      }
      
      if (target.isAssignableFrom(_sourceType)) {
        return (Convert) IDENTITY;
      }
      
      ConvertException exn
        = new ConvertException(L.l("Can't convert {0} to {1}",
                                   _sourceType.getName(),
                                   target.getName()));
      
      return (Convert) new ConvertWithException(exn);
    }
    
    @Override
    public String toString()
    {
      return getClass().getSimpleName() + "[" + sourceType().getSimpleName() + "]";
    }
  }
  
  static final class ConverterIdentity implements Convert
  {
    @Override
    public T convert(T source)
    {
      return source;
    }
  }
}