TypeBindingsTest.java

package com.fasterxml.jackson.databind.type;

import java.util.*;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.testutil.DatabindTestUtil;

import static org.junit.jupiter.api.Assertions.*;

/**
 * Simple tests to verify for generic type binding functionality
 * implemented by {@link TypeBindings} class.
 */
public class TypeBindingsTest extends DatabindTestUtil
{
    static class AbstractType<A,B> { }

    static class LongStringType extends AbstractType<Long,String> { }

    static class InnerGenericTyping<K, V> extends AbstractMap<K, Collection<V>>
    {
        @Override
        public Set<java.util.Map.Entry<K, Collection<V>>> entrySet() {
            return null;
        }
        public class InnerClass extends AbstractMap<K, Collection<V>> {
            @Override
            public Set<java.util.Map.Entry<K, Collection<V>>> entrySet() {
                return null;
            }
        }
    }

    // for [databind#76]
    @SuppressWarnings("serial")
    static class HashTree<K, V> extends HashMap<K, HashTree<K, V>> { }

    /*
    /**********************************************************
    /* Test methods
    /**********************************************************
     */

    private final TypeFactory DEFAULT_TF = defaultTypeFactory();

    @Test
    public void testInnerType() throws Exception
    {
        JavaType type = DEFAULT_TF.constructType(InnerGenericTyping.InnerClass.class);
        assertEquals(MapType.class, type.getClass());
        JavaType keyType = type.getKeyType();
        assertEquals(Object.class, keyType.getRawClass());
        JavaType valueType = type.getContentType();
        assertEquals(Collection.class, valueType.getRawClass());
        JavaType vt2 = valueType.getContentType();
        assertEquals(Object.class, vt2.getRawClass());
    }

    // for [databind#76]
    @Test
    public void testRecursiveType()
    {
        JavaType type = DEFAULT_TF.constructType(HashTree.class);
        assertNotNull(type);
    }

    @Test
    public void testBindingsBasics()
    {
        TypeBindings b = TypeBindings.create(Collection.class,
                TypeFactory.unknownType());
        // let's just call it -- should probably try to inspect but...
        assertNotNull(b.toString());
        assertEquals(Object.class, b.getBoundType(0).getRawClass());
        assertNull(b.getBoundName(-1));
        assertNull(b.getBoundType(-1));
        assertNull(b.getBoundName(1));
        assertNull(b.getBoundType(1));

        assertFalse(b.equals("foo"));
    }

    @Test
    public void testInvalidBindings()
    {
        JavaType unknown = TypeFactory.unknownType();
        try {
            TypeBindings.create(AbstractType.class, unknown);
            fail("Should not pass");
        } catch (IllegalArgumentException e) {
            verifyException(e, "Cannot create TypeBindings");
            verifyException(e, "class expects 2");
        }
    }

    // for [databind#3876]
    @Test
    public void testEqualityAndHashCode()
    {
        JavaType stringType = DEFAULT_TF.constructType(String.class);
        JavaType integerType = DEFAULT_TF.constructType(Integer.class);
        TypeBindings listStringBindings = TypeBindings.create(List.class, stringType);
        TypeBindings listStringBindingsWithUnbound = listStringBindings.withUnboundVariable("X");
        TypeBindings iterableStringBindings = TypeBindings.create(Iterable.class, stringType);
        TypeBindings mapStringInt = TypeBindings.create(Map.class, stringType, integerType);
        TypeBindings mapIntString = TypeBindings.create(Map.class, integerType, stringType);
        // Ensure that type variable names used by List and Iterable do not change in future java versions
        assertEquals("E", listStringBindings.getBoundName(0));
        assertEquals("T", iterableStringBindings.getBoundName(0));
        // These TypeBindings bind the same types in the same order
        assertEquals(listStringBindings, iterableStringBindings);
        assertEquals(listStringBindings.hashCode(), iterableStringBindings.hashCode());
        // Type bindings which differ by an unbound variable still evaluate to equal
        assertEquals(listStringBindingsWithUnbound, listStringBindings);
        assertEquals(listStringBindingsWithUnbound.hashCode(), listStringBindings.hashCode());
        // However type bindings for the same types in different order must differ:
        assertNotEquals(mapStringInt, mapIntString);
        assertNotEquals(mapStringInt.hashCode(), mapIntString.hashCode());

        Object iterableStringBaseList = iterableStringBindings.asKey(List.class);
        Object listStringBaseList = listStringBindings.asKey(List.class);
        Object listStringBindingsWithUnboundBaseList = listStringBindingsWithUnbound.asKey(List.class);
        Object mapStringIntBaseMap = mapStringInt.asKey(Map.class);
        Object mapIntStringBaseMap = mapIntString.asKey(Map.class);
        assertEquals(iterableStringBaseList, listStringBaseList);
        assertEquals(iterableStringBaseList.hashCode(), listStringBaseList.hashCode());
        assertEquals(listStringBindingsWithUnboundBaseList, listStringBaseList);
        assertEquals(listStringBindingsWithUnboundBaseList.hashCode(), listStringBaseList.hashCode());
        assertNotEquals(mapStringIntBaseMap, mapIntStringBaseMap);
        assertNotEquals(mapStringIntBaseMap.hashCode(), mapIntStringBaseMap.hashCode());
    }
}