@@ -15,9 +15,16 @@ import com.fasterxml.jackson.datatype.joda.JodaModule;
15
15
{ {#threetenbp} }
16
16
import com.fasterxml.jackson.datatype.threetenbp.ThreeTenModule;
17
17
{ {/threetenbp} }
18
+ { {#models.0} }
19
+ import { {modelPackage} }.*;
20
+ { {/models.0} }
18
21
19
22
import java.text.DateFormat;
20
-
23
+ import java.util.HashMap;
24
+ import java.util.HashSet;
25
+ import java.util.Map;
26
+ import java.util.Set;
27
+ import javax.ws.rs.core.GenericType;
21
28
import javax.ws.rs.ext.ContextResolver;
22
29
23
30
{ {> generatedAnnotation} }
@@ -69,4 +76,180 @@ public class JSON implements ContextResolver<ObjectMapper> {
69
76
* @return object mapper
70
77
*/
71
78
public ObjectMapper getMapper() { return mapper; }
79
+
80
+ /**
81
+ * Returns the target model class that should be used to deserialize the input data.
82
+ * The discriminator mappings are used to determine the target model class.
83
+ *
84
+ * @param node The input data.
85
+ * @param modelClass The class that contains the discriminator mappings.
86
+ */
87
+ public static Class getClassForElement(JsonNode node, Class modelClass) {
88
+ ClassDiscriminatorMapping cdm = modelDiscriminators.get(modelClass);
89
+ if (cdm != null) {
90
+ return cdm.getClassForElement(node, new HashSet< Class> ());
91
+ }
92
+ return null;
93
+ }
94
+
95
+ /**
96
+ * Helper class to register the discriminator mappings.
97
+ */
98
+ private static class ClassDiscriminatorMapping {
99
+ // The model class name.
100
+ Class modelClass;
101
+ // The name of the discriminator property.
102
+ String discriminatorName;
103
+ // The discriminator mappings for a model class.
104
+ Map< String, Class> discriminatorMappings;
105
+
106
+ // Constructs a new class discriminator.
107
+ ClassDiscriminatorMapping(Class cls, String name) {
108
+ modelClass = cls;
109
+ discriminatorName = name;
110
+ discriminatorMappings = new HashMap< String, Class> ();
111
+ }
112
+
113
+ // Register a discriminator mapping for the specified model class.
114
+ void registerMapping(String mapping, Class cls) {
115
+ discriminatorMappings.put(mapping, cls);
116
+ }
117
+
118
+ // Return the name of the discriminator property for this model class.
119
+ String getDiscriminatorPropertyName() {
120
+ return discriminatorName;
121
+ }
122
+
123
+ // Return the discriminator value or null if the discriminator is not
124
+ // present in the payload.
125
+ String getDiscriminatorValue(JsonNode node) {
126
+ // Determine the value of the discriminator property in the input data.
127
+ if (discriminatorName != null) {
128
+ // Get the value of the discriminator property, if present in the input payload.
129
+ node = node.get(discriminatorName);
130
+ if (node != null && node.isValueNode()) {
131
+ String discrValue = node.asText();
132
+ if (discrValue != null) {
133
+ return discrValue;
134
+ }
135
+ }
136
+ }
137
+ return null;
138
+ }
139
+
140
+ /**
141
+ * Returns the target model class that should be used to deserialize the input data.
142
+ * This function can be invoked for anyOf/oneOf composed models with discriminator mappings.
143
+ * The discriminator mappings are used to determine the target model class.
144
+ *
145
+ * @param node The input data.
146
+ * @param visitedClasses The set of classes that have already been visited.
147
+ */
148
+ Class getClassForElement(JsonNode node, Set<Class > visitedClasses) {
149
+ if (visitedClasses.contains(modelClass)) {
150
+ // Class has already been visited.
151
+ return null;
152
+ }
153
+ // Determine the value of the discriminator property in the input data.
154
+ String discrValue = getDiscriminatorValue(node);
155
+ if (discrValue == null) {
156
+ return null;
157
+ }
158
+ Class cls = discriminatorMappings.get(discrValue);
159
+ // It may not be sufficient to return this cls directly because that target class
160
+ // may itself be a composed schema, possibly with its own discriminator.
161
+ visitedClasses.add(modelClass);
162
+ for (Class childClass : discriminatorMappings.values()) {
163
+ ClassDiscriminatorMapping childCdm = modelDiscriminators.get(childClass);
164
+ if (childCdm == null) {
165
+ continue;
166
+ }
167
+ if (!discriminatorName.equals(childCdm.discriminatorName)) {
168
+ discrValue = getDiscriminatorValue(node);
169
+ if (discrValue == null) {
170
+ continue;
171
+ }
172
+ }
173
+ if (childCdm != null) {
174
+ // Recursively traverse the discriminator mappings.
175
+ Class childDiscr = childCdm.getClassForElement(node, visitedClasses);
176
+ if (childDiscr != null) {
177
+ return childDiscr;
178
+ }
179
+ }
180
+ }
181
+ return cls;
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Returns true if inst is an instance of modelClass in the OpenAPI model hierarchy.
187
+ *
188
+ * The Java class hierarchy is not implemented the same way as the OpenAPI model hierarchy,
189
+ * so it's not possible to use the instanceof keyword.
190
+ *
191
+ * @param modelClass A OpenAPI model class.
192
+ * @param inst The instance object.
193
+ */
194
+ public static boolean isInstanceOf(Class modelClass, Object inst, Set<Class > visitedClasses) {
195
+ if (modelClass.isInstance(inst)) {
196
+ // This handles the ' allOf' use case with single parent inheritance.
197
+ return true ;
198
+ }
199
+ if (visitedClasses.contains(modelClass)) {
200
+ // This is to prevent infinite recursion when the composed schemas have
201
+ // a circular dependency.
202
+ return false ;
203
+ }
204
+ visitedClasses.add(modelClass);
205
+
206
+ // Traverse the oneOf/anyOf composed schemas.
207
+ Map<String , GenericType > descendants = modelDescendants.get(modelClass);
208
+ if (descendants != null) {
209
+ for (GenericType childType : descendants.values()) {
210
+ if (isInstanceOf(childType.getRawType(), inst, visitedClasses)) {
211
+ return true ;
212
+ }
213
+ }
214
+ }
215
+ return false;
216
+ }
217
+
218
+ private static Map<Class , ClassDiscriminatorMapping > modelDiscriminators = new HashMap<Class , ClassDiscriminatorMapping >();
219
+
220
+ /**
221
+ * Register the discriminators for all composed models.
222
+ */
223
+ private static void registerDiscriminators() {
224
+ {{#models} }
225
+ { {#model} }
226
+ { {#discriminator} }
227
+ {
228
+ // Initialize the discriminator mappings for ' {{classname}}' .
229
+ ClassDiscriminatorMapping m = new ClassDiscriminatorMapping({{classname} }.class, "{ {propertyBaseName} }");
230
+ { {#mappedModels} }
231
+ m.registerMapping("{ {mappingName} }", { {modelName} }.class);
232
+ { {/mappedModels} }
233
+ m.registerMapping("{ {name} }", { {classname} }.class);
234
+ modelDiscriminators.put({ {classname} }.class, m);
235
+ }
236
+ { {/discriminator} }
237
+ { {/model} }
238
+ { {/models} }
239
+ }
240
+
241
+ private static Map<Class , Map <String, GenericType >> modelDescendants = new HashMap<Class , Map <String, GenericType >>();
242
+
243
+ /**
244
+ * Register the oneOf/anyOf descendants.
245
+ * TODO: this should not be a public method.
246
+ */
247
+ public static void registerDescendants(Class modelClass, Map<String , GenericType > descendants) {
248
+ modelDescendants.put(modelClass, descendants);
249
+ }
250
+
251
+ static {
252
+ registerDiscriminators();
253
+ }
254
+
72
255
}
0 commit comments