NestedTypes1604Test.java

package com.fasterxml.jackson.databind.type;

import java.util.ArrayList;
import java.util.List;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.databind.*;

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

import static com.fasterxml.jackson.databind.testutil.DatabindTestUtil.newJsonMapper;

// for [databind#1604]
public class NestedTypes1604Test
{
    public static class Data<T> {
        private T data;

        public Data(T data) {
             this.data = data;
        }

        public T getData() {
             return data;
        }

        public static <T> Data<List<T>> of(List<T> data) {
             return new DataList<>(data);
        }

        public static <T> Data<List<T>> ofRefined(List<T> data) {
            return new RefinedDataList<>(data);
        }

        public static <T> Data<List<T>> ofSneaky(List<T> data) {
            return new SneakyDataList<String,T>(data);
        }
    }

    public static class DataList<T> extends Data<List<T>> {
        public DataList(List<T> data) {
            super(data);
        }
    }

    // And then add one level between types
    public static class RefinedDataList<T> extends DataList<T> {
        public RefinedDataList(List<T> data) {
            super(data);
        }
    }

    // And/or add another type parameter that is not relevant (less common
    // but potential concern)
    public static class SneakyDataList<BOGUS,T> extends Data<List<T>> {
        public SneakyDataList(List<T> data) {
            super(data);
        }
    }

    public static class Inner {
        private int index;

        public Inner(int index) {
             this.index = index;
        }

        public int getIndex() {
             return index;
        }
    }

    public static class BadOuter {
        private Data<List<Inner>> inner;

        public BadOuter(Data<List<Inner>> inner) {
            this.inner = inner;
        }

        public Data<List<Inner>> getInner() {
            return inner;
        }
    }

    public static class GoodOuter {
        private DataList<Inner> inner;

        public GoodOuter(DataList<Inner> inner) {
            this.inner = inner;
        }

        public DataList<Inner> getInner() {
            return inner;
        }
    }

    private final ObjectMapper objectMapper = newJsonMapper();

    @Test
    public void testIssue1604Simple() throws Exception
    {
        List<Inner> inners = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            inners.add(new Inner(i));
        }
        BadOuter badOuter = new BadOuter(Data.of(inners));
//        GoodOuter goodOuter = new GoodOuter(new DataList<>(inners));
//        String json = objectMapper.writeValueAsString(goodOuter);

        // 11-Oct-2017, tatu: Fails with exception wrt type specialization
        String json = objectMapper.writeValueAsString(badOuter);
        assertNotNull(json);
   }

   @Test
    public void testIssue1604Subtype() throws Exception
    {
        List<Inner> inners = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            inners.add(new Inner(i));
        }
        BadOuter badOuter = new BadOuter(Data.ofRefined(inners));
        String json = objectMapper.writeValueAsString(badOuter);
        assertNotNull(json);
   }

   @Test
    public void testIssue1604Sneaky() throws Exception
    {
        List<Inner> inners = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            inners.add(new Inner(i));
        }
        BadOuter badOuter = new BadOuter(Data.ofSneaky(inners));
        String json = objectMapper.writeValueAsString(badOuter);
        assertNotNull(json);
   }
}