content: /*
   * ConverterFactory.java January 2010
   *
   * Copyright (C) 2010, Niall Gallagher <niallg@users.sf.net>
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
   * implied. See the License for the specific language governing 
   * permissions and limitations under the License.
   */
  
  package com.aliyun.odps.simpleframework.xml.convert;
  
  import com.aliyun.odps.simpleframework.xml.util.Cache;
  import java.lang.reflect.Constructor;
  
  import com.aliyun.odps.simpleframework.xml.util.ConcurrentCache;
  
  /**
   * The <code>ConverterFactory</code> is used to instantiate objects
   * based on a provided type or annotation. This provides a single
   * point of creation for all converters within the framework. For
   * performance all the instantiated converters are cached against
   * the class for that converter. This ensures the converters can
   * be acquired without the overhead of instantiation.
   * 
   * @author Niall Gallagher
   * 
   * @see org.simpleframework.xml.convert.ConverterCache
   */
  class ConverterFactory {
     
     /**
      * This is the cache that is used to cache converter instances.
      */
     private final Cache<Converter> cache;
     
     /**
      * Constructor for the <code>ConverterFactory</code> object. 
      * This will create an internal cache which is used to cache all
      * instantiations made by the factory. Caching the converters
      * ensures there is no overhead with instantiations.
      */
     public ConverterFactory() {
        this.cache = new ConcurrentCache<Converter>();
     }
     
     /**
      * This is used to instantiate the converter based on the type
      * provided. If the type provided can not be instantiated for
      * some reason then an exception is thrown from this method.
      * 
      * @param type this is the converter type to be instantiated
      * 
      * @return this returns an instance of the provided type
      */
     public Converter getInstance(Class type) throws Exception {
        Converter converter = cache.fetch(type);
        
        if(converter == null) {
           return getConverter(type);
        }
        return converter;
     }
     
     /**
      * This is used to instantiate the converter based on the type
      * of the <code>Convert</code> annotation provided. If the type 
      * can not be instantiated for some reason then an exception is 
      * thrown from this method.
      * 
      * @param convert this is the annotation containing the type
      * 
      * @return this returns an instance of the provided type
      */
     public Converter getInstance(Convert convert) throws Exception {
        Class type = convert.value();
        
        if(type.isInterface()) {
           throw new ConvertException("Can not instantiate %s", type);
        }
        return getInstance(type);
     }
     
     /**
      * This is used to instantiate the converter based on the type
      * provided. If the type provided can not be instantiated for
      * some reason then an exception is thrown from this method.
      * 
      * @param type this is the converter type to be instantiated
      * 
      * @return this returns an instance of the provided type
      */
     private Converter getConverter(Class type) throws Exception {
        Constructor factory = getConstructor(type);
        
        if(factory == null){
           throw new ConvertException("No default constructor for %s", type);
        }
        return getConverter(type, factory);
     }
     
     /**
      * This is used to instantiate the converter based on the type
      * provided. If the type provided can not be instantiated for
      * some reason then an exception is thrown from this method.
      * 
      * @param type this is the converter type to be instantiated
      * @param factory this is the constructor used to instantiate
      * 
      * @return this returns an instance of the provided type
      */
     private Converter getConverter(Class type, Constructor factory) throws Exception {
        Converter converter = (Converter)factory.newInstance();
        
        if(converter != null){
           cache.cache(type, converter);
        }
        return converter;
     }
     
     /**
      * This is used to acquire the default no argument constructor
      * for the the provided type. If the constructor is not accessible
      * then it will be made accessible so that it can be instantiated.
      * 
      * @param type this is the type to acquire the constructor for
      * 
      * @return this returns the constructor for the type provided
      */
     private Constructor getConstructor(Class type) throws Exception {
        Constructor factory = type.getDeclaredConstructor();
        
        if(!factory.isAccessible()) {
           factory.setAccessible(true);
        }
        return factory;
     }
  }
  