blob: 1b29a77bb9df96b5256153ca47edc3e67c65180c [file] [log] [blame]
Tatu Saloranta0664ac62018-04-25 15:41:19 -07001package com.fasterxml.jackson.databind.jsontype;
Tatu Saloranta09e5ba12018-04-05 20:14:35 -07002
3import java.util.*;
4
Tatu Salorantabfeb1fa2018-05-14 16:37:35 -07005import com.fasterxml.jackson.annotation.JsonProperty;
Tatu Saloranta771f6c62020-02-27 19:52:22 -08006import com.fasterxml.jackson.annotation.JsonSubTypes;
7import com.fasterxml.jackson.annotation.JsonTypeInfo;
8
Tatu Saloranta09e5ba12018-04-05 20:14:35 -07009import com.fasterxml.jackson.databind.*;
10
Tatu Salorantade6ca252018-04-19 15:01:51 -070011/**
Tatu Saloranta771f6c62020-02-27 19:52:22 -080012 * Originally test for [databind#1964], wherein slightly incompatible type hierarchy,
Tatu Salorantade6ca252018-04-19 15:01:51 -070013 * where `Map` key is downcast from `String` to `Object` (via use of "raw"
14 * types to force compiler to ignore incompatibility) causes exception
15 * during serialization. Although ideally code would not force round peg
16 * through square hole, it makes sense to add specific exception to allow
17 * such downcast just for Map key types (for now at least).
18 */
Tatu Saloranta09e5ba12018-04-05 20:14:35 -070019@SuppressWarnings("serial")
Tatu Saloranta771f6c62020-02-27 19:52:22 -080020public class SubTypeResolutionTest extends BaseMapTest
Tatu Saloranta09e5ba12018-04-05 20:14:35 -070021{
Tatu Salorantabfeb1fa2018-05-14 16:37:35 -070022 // [databind#1964]
Tatu Saloranta09e5ba12018-04-05 20:14:35 -070023 static class AccessModel {
Tatu Salorantade6ca252018-04-19 15:01:51 -070024 private Map<String, Collection<String>> repositoryPrivileges;
25
Tatu Saloranta09e5ba12018-04-05 20:14:35 -070026 public AccessModel() {
27 repositoryPrivileges = new HashMap<>();
28 }
Tatu Salorantade6ca252018-04-19 15:01:51 -070029
30 // 19-Apr-2018, tatu; this would prevent issues
31// @JsonSerialize(typing = JsonSerialize.Typing.STATIC)
32 public Map<String, Collection<String>> getRepositoryPrivileges() {
Tatu Saloranta09e5ba12018-04-05 20:14:35 -070033 return repositoryPrivileges;
34 }
Tatu Salorantade6ca252018-04-19 15:01:51 -070035
36 public void setRepositoryPrivileges(Map<String, Collection<String>> repositoryPrivileges) {
Tatu Saloranta09e5ba12018-04-05 20:14:35 -070037 this.repositoryPrivileges = repositoryPrivileges;
38 }
39 }
Tatu Saloranta09e5ba12018-04-05 20:14:35 -070040 static class CustomMap<T> extends LinkedHashMap<Object, T> { }
41
Tatu Salorantabfeb1fa2018-05-14 16:37:35 -070042 // [databind#2034]: specialization from `Object` to other types probably should
43 // just be allowed (at least in serialization case)
44 interface Dummy {
45 List<String> getStrings();
46 }
47
48 static class MetaModel<M, B> extends AbstractMetaValue<M, M, B> {
49
50 @JsonProperty
51 protected final Map<String, AbstractMetaValue<M, ?, B>> attributes = new HashMap<>();
52
53 public <V> ListMetaAttribute<M, V, B> describeList(final String attributeName) {
54 final ListMetaAttribute<M, V, B> metaAttribute = new ListMetaAttribute<>();
55 attributes.put(attributeName, metaAttribute);
56 return metaAttribute;
57 }
58 }
59
60 static abstract class AbstractMetaValue<M, V, B> {
61 public int getBogus() { return 3; }
62 }
63
64 static class ListMetaAttribute<M, V, B> extends MetaAttribute<M, List<V>, B> {
65 public ListMetaAttribute() { }
66 }
67
68 static class MetaAttribute<M, V, B> extends AbstractMetaValue<M, V, B> {
69 public MetaAttribute() { }
70 }
Tatu Saloranta771f6c62020-02-27 19:52:22 -080071
72 // [databind#2632]: fail to specialize type-erased
73 @SuppressWarnings("rawtypes")
74 @JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
75 @JsonSubTypes(value = {
76 @JsonSubTypes.Type(value = Either.Left.class, name = "left"),
77 @JsonSubTypes.Type(value = Either.Right.class, name = "right")
78 })
79 static class Either<L, R> {
80 static class Left<T> extends Either { }
81 static class Right<T> extends Either { }
82 }
83
84 static class Foo {
85 @SuppressWarnings("unchecked")
86 public Either<String, String> getEither() {
87 return new Either.Right<String>();
88 }
89 }
90
Tatu Salorantabfeb1fa2018-05-14 16:37:35 -070091 /*
92 /**********************************************************************
93 /* Unit tests
94 /**********************************************************************
95 */
96
Tatu Salorantac7ddf692019-08-23 14:02:52 -070097 final ObjectMapper MAPPER = newJsonMapper();
Tatu Salorantabfeb1fa2018-05-14 16:37:35 -070098
99 // [databind#1964]
Tatu Saloranta09e5ba12018-04-05 20:14:35 -0700100 public void testTypeCompatibility1964() throws Exception
101 {
Tatu Salorantade6ca252018-04-19 15:01:51 -0700102 // Important! Must use raw type since assignment requires effectively
103 // casting due incompatible type parameters.
Tatu Salorantafbe64a22018-05-03 20:40:56 -0700104 @SuppressWarnings({ "unchecked", "rawtypes" })
Tatu Salorantade6ca252018-04-19 15:01:51 -0700105 Map<String, Collection<String>> repoPrivilegesMap = new CustomMap();
Tatu Saloranta09e5ba12018-04-05 20:14:35 -0700106 String key = "/storages/storage0/releases";
107 Collection<String> values = new HashSet<>();
108 values.add("ARTIFACTS_RESOLVE");
109 repoPrivilegesMap.put(key, values);
110
111 AccessModel accessModel = new AccessModel();
112 accessModel.setRepositoryPrivileges(repoPrivilegesMap);
113
Tatu Salorantabfeb1fa2018-05-14 16:37:35 -0700114 String jsonStr = MAPPER.writeValueAsString(accessModel);
115 // ... could/should verify more, perhaps, but for now let it be.
116 assertNotNull(jsonStr);
117 }
118
119 // [databind#2034]
120 public void testTypeSpecialization2034() throws Exception
121 {
122 MetaModel<Dummy, Dummy> metaModel = new MetaModel<>();
123 metaModel.describeList("a1");
124 String jsonStr = MAPPER.writeValueAsString(metaModel);
Tatu Salorantade6ca252018-04-19 15:01:51 -0700125 // ... could/should verify more, perhaps, but for now let it be.
Tatu Saloranta09e5ba12018-04-05 20:14:35 -0700126 assertNotNull(jsonStr);
127 }
Tatu Saloranta771f6c62020-02-27 19:52:22 -0800128
129 // [databind#2632]: fail to specialize type-erased
130 public void testSpecializeIncompatibleRawType() throws Exception
131 {
132 // 27-Feb-2020, tatu: First things first; incompatible typing should
133 // cause reasonable exception
134 // ... although since it's writing, perhaps should NOT fail at all?
135 String json;
136
137 try {
138 json = MAPPER.writeValueAsString(new Foo());
139 assertNotNull(json);
140 fail("Should not (yet?) pass");
141 } catch (JsonMappingException e) {
142 verifyException(e, "Failed to specialize base type ");
143 }
144 }
Tatu Saloranta09e5ba12018-04-05 20:14:35 -0700145}