/**
 * Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org>
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package org.seedstack.coffig.mapper;

import org.seedstack.coffig.ConfigurationException;
import org.seedstack.coffig.TreeNode;
import org.seedstack.coffig.node.ValueNode;
import org.seedstack.coffig.spi.ConfigurationMapper;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;

public class ClassMapper implements ConfigurationMapper {
    @Override
    public boolean canHandle(Type type) {
        if (type instanceof ParameterizedType) {
            Type rawType = ((ParameterizedType) type).getRawType();
            if (rawType instanceof Class) {
                return Class.class.isAssignableFrom(((Class<?>) rawType));
            }
        }
        return false;
    }

    @Override
    public Object map(TreeNode treeNode, Type type) {
        Type itemType = ((ParameterizedType) type).getActualTypeArguments()[0];
        Class<?> aClass;
        try {
            aClass = Class.forName(treeNode.value());
        } catch (ClassNotFoundException e) {
            throw new ConfigurationException("Unable to load class <" + treeNode.value() + ">");
        }

        if (itemType instanceof Class && ((Class<?>) itemType).isAssignableFrom(aClass)) {
            return aClass;
        } else if (itemType instanceof WildcardType && isSatisfyingBounds(aClass, (WildcardType) itemType)) {
            return aClass;
        } else {
            throw new ConfigurationException("Class<" + aClass.getCanonicalName() + "> is not assignable to Class<" + itemType.getTypeName() + ">");
        }
    }

    @Override
    public TreeNode unmap(Object object, Type type) {
        return new ValueNode(((Class<?>) object).getCanonicalName());
    }

    private boolean isSatisfyingBounds(Class<?> aClass, WildcardType wildcardType) {
        for (Type bound : wildcardType.getUpperBounds()) {
            if (!((Class<?>) bound).isAssignableFrom(aClass)) {
                return false;
            }
        }
        for (Type bound : wildcardType.getLowerBounds()) {
            if (!(aClass.isAssignableFrom((Class<?>) bound))) {
                return false;
            }
        }
        return true;
    }
//
//    private Class getRawType(Type type) {
//        if (type instanceof Class) {
//            return ((Class<?>) type);
//        } else if (type instanceof ParameterizedType) {
//            return (Class) ((ParameterizedType) type).getRawType();
//        }
//    }
}

@@ -2162,33 +2162,34 @@
   if (!(
-(Class%3C?%3E) 
+getRawType(
 bound)
+)
 .isAssig
@@ -2368,27 +2368,27 @@
 rom(
-(Class%3C?%3E) 
+getRawType(
 bound)))
  %7B%0A 
@@ -2383,16 +2383,17 @@
 bound)))
+)
  %7B%0A     
@@ -2472,13 +2472,9 @@
   %7D%0A
-//%0A//
+%0A
     
@@ -2486,16 +2486,19 @@
 te Class
+%3C?%3E
  getRawT
@@ -2510,26 +2510,24 @@
 ype type) %7B%0A
-//
         if (
@@ -2547,26 +2547,24 @@
 of Class) %7B%0A
-//
             
@@ -2589,18 +2589,16 @@
  type);%0A
-//
         
@@ -2645,18 +2645,16 @@
 Type) %7B%0A
-//
         
@@ -2668,16 +2668,19 @@
 urn 
-(Class) 
+getRawType(
 ((Pa
@@ -2718,12 +2718,11 @@
 pe()
+)
 ;%0A
-//
     
@@ -2730,11 +2730,124 @@
    %7D
-%0A//
+ else %7B%0A            throw new ConfigurationException(%22Cannot resolve raw type of %22 + type.getTypeName());%0A        %7D%0A
     

Fix bound checking in class mapper