24
24
import java .util .stream .Collectors ;
25
25
26
26
import org .bson .Document ;
27
+
27
28
import org .springframework .data .mapping .PersistentProperty ;
28
29
import org .springframework .data .mapping .context .MappingContext ;
29
30
import org .springframework .data .mongodb .core .convert .MongoConverter ;
40
41
import org .springframework .data .mongodb .core .schema .MongoJsonSchema ;
41
42
import org .springframework .data .mongodb .core .schema .MongoJsonSchema .MongoJsonSchemaBuilder ;
42
43
import org .springframework .data .mongodb .core .schema .TypedJsonSchemaObject ;
43
- import org .springframework .lang . Nullable ;
44
+ import org .springframework .data . util . ClassTypeInformation ;
44
45
import org .springframework .util .Assert ;
45
46
import org .springframework .util .ClassUtils ;
46
47
import org .springframework .util .CollectionUtils ;
@@ -168,7 +169,7 @@ private JsonSchemaProperty computeSchemaForProperty(List<MongoPersistentProperty
168
169
169
170
JsonSchemaProperty schemaProperty ;
170
171
if (isCollection (property )) {
171
- schemaProperty = createSchemaPropertyForCollection (fieldName , property , required );
172
+ schemaProperty = createArraySchemaProperty (fieldName , property , required );
172
173
} else if (property .isMap ()) {
173
174
schemaProperty = createSchemaProperty (fieldName , Type .objectType (), required );
174
175
} else if (ClassUtils .isAssignable (Enum .class , targetType )) {
@@ -180,49 +181,52 @@ private JsonSchemaProperty computeSchemaForProperty(List<MongoPersistentProperty
180
181
return applyEncryptionDataIfNecessary (property , schemaProperty );
181
182
}
182
183
183
- private JsonSchemaProperty createSchemaPropertyForCollection (String fieldName , MongoPersistentProperty property ,
184
+ private JsonSchemaProperty createArraySchemaProperty (String fieldName , MongoPersistentProperty property ,
184
185
boolean required ) {
185
186
186
187
ArrayJsonSchemaProperty schemaProperty = JsonSchemaProperty .array (fieldName );
187
188
188
- if (property .getActualType () != Object .class ) {
189
+ if (isSpecificType (property )) {
190
+ schemaProperty = potentiallyEnhanceArraySchemaProperty (property , schemaProperty );
191
+ }
189
192
190
- MongoPersistentEntity <?> persistentEntity = mappingContext
191
- . getPersistentEntity ( property . getTypeInformation (). getComponentType ());
193
+ return createPotentiallyRequiredSchemaProperty ( schemaProperty , required );
194
+ }
192
195
193
- if (persistentEntity == null ) {
196
+ @ SuppressWarnings ({ "unchecked" , "rawtypes" })
197
+ private ArrayJsonSchemaProperty potentiallyEnhanceArraySchemaProperty (MongoPersistentProperty property ,
198
+ ArrayJsonSchemaProperty schemaProperty ) {
194
199
195
- if (ClassUtils .isAssignable (Enum .class , property .getActualType ())) {
200
+ MongoPersistentEntity <?> persistentEntity = mappingContext
201
+ .getPersistentEntity (property .getTypeInformation ().getRequiredComponentType ());
196
202
197
- List < Object > possibleValues = new ArrayList <>();
203
+ if ( persistentEntity != null ) {
198
204
199
- for (Object enumValue : EnumSet .allOf ((Class ) property .getActualType ())) {
200
- possibleValues .add (converter .convertToMongoType (enumValue ));
201
- }
205
+ List <JsonSchemaProperty > nestedProperties = computePropertiesForEntity (Collections .emptyList (), persistentEntity );
202
206
203
- Class targetType = possibleValues .isEmpty () ? property .getActualType ()
204
- : possibleValues .iterator ().next ().getClass ();
205
- schemaProperty = schemaProperty
206
- .items (Collections .singleton (JsonSchemaObject .of (targetType ).possibleValues (possibleValues )));
207
- } else {
208
- schemaProperty = schemaProperty .items (Collections .singleton (JsonSchemaObject .of (property .getActualType ())));
209
- }
210
- } else {
207
+ if (nestedProperties .isEmpty ()) {
208
+ return schemaProperty ;
209
+ }
211
210
212
- List <JsonSchemaProperty > nestedProperties = computePropertiesForEntity (Collections .emptyList (),
213
- persistentEntity );
211
+ return schemaProperty
212
+ .items (JsonSchemaObject .object ().properties (nestedProperties .toArray (new JsonSchemaProperty [0 ])));
213
+ }
214
214
215
- if (!nestedProperties .isEmpty ()) {
216
- schemaProperty = schemaProperty .items (Collections
217
- .singleton (JsonSchemaObject .object ().properties (nestedProperties .toArray (new JsonSchemaProperty [0 ]))));
218
- }
219
- }
215
+ if (ClassUtils .isAssignable (Enum .class , property .getActualType ())) {
216
+
217
+ List <Object > possibleValues = getPossibleEnumValues ((Class <Enum >) property .getActualType ());
218
+
219
+ return schemaProperty
220
+ .items (createSchemaObject (computeTargetType (property .getActualType (), possibleValues ), possibleValues ));
220
221
}
221
222
222
- return createPotentiallyRequiredSchemaProperty (schemaProperty , required );
223
+ return schemaProperty .items (JsonSchemaObject .of (property .getActualType ()));
224
+ }
225
+
226
+ private boolean isSpecificType (MongoPersistentProperty property ) {
227
+ return !ClassTypeInformation .OBJECT .equals (property .getTypeInformation ().getActualType ());
223
228
}
224
229
225
- @ Nullable
226
230
private JsonSchemaProperty applyEncryptionDataIfNecessary (MongoPersistentProperty property ,
227
231
JsonSchemaProperty schemaProperty ) {
228
232
@@ -252,15 +256,12 @@ private JsonSchemaProperty createObjectSchemaPropertyForEntity(List<MongoPersist
252
256
target .properties (nestedProperties .toArray (new JsonSchemaProperty [0 ])), required );
253
257
}
254
258
259
+ @ SuppressWarnings ({ "unchecked" , "rawtypes" })
255
260
private JsonSchemaProperty createEnumSchemaProperty (String fieldName , Class <?> targetType , boolean required ) {
256
261
257
- List <Object > possibleValues = new ArrayList <>( );
262
+ List <Object > possibleValues = getPossibleEnumValues (( Class < Enum >) targetType );
258
263
259
- for (Object enumValue : EnumSet .allOf ((Class ) targetType )) {
260
- possibleValues .add (converter .convertToMongoType (enumValue ));
261
- }
262
-
263
- targetType = possibleValues .isEmpty () ? targetType : possibleValues .iterator ().next ().getClass ();
264
+ targetType = computeTargetType (targetType , possibleValues );
264
265
return createSchemaProperty (fieldName , targetType , required , possibleValues );
265
266
}
266
267
@@ -271,14 +272,20 @@ JsonSchemaProperty createSchemaProperty(String fieldName, Object type, boolean r
271
272
JsonSchemaProperty createSchemaProperty (String fieldName , Object type , boolean required ,
272
273
Collection <?> possibleValues ) {
273
274
275
+ TypedJsonSchemaObject schemaObject = createSchemaObject (type , possibleValues );
276
+
277
+ return createPotentiallyRequiredSchemaProperty (JsonSchemaProperty .named (fieldName ).with (schemaObject ), required );
278
+ }
279
+
280
+ private TypedJsonSchemaObject createSchemaObject (Object type , Collection <?> possibleValues ) {
281
+
274
282
TypedJsonSchemaObject schemaObject = type instanceof Type ? JsonSchemaObject .of (Type .class .cast (type ))
275
283
: JsonSchemaObject .of (Class .class .cast (type ));
276
284
277
285
if (!CollectionUtils .isEmpty (possibleValues )) {
278
286
schemaObject = schemaObject .possibleValues (possibleValues );
279
287
}
280
-
281
- return createPotentiallyRequiredSchemaProperty (JsonSchemaProperty .named (fieldName ).with (schemaObject ), required );
288
+ return schemaObject ;
282
289
}
283
290
284
291
private String computePropertyFieldName (PersistentProperty property ) {
@@ -309,23 +316,34 @@ private Class<?> computeTargetType(PersistentProperty<?> property) {
309
316
return mongoProperty .getFieldType () != mongoProperty .getActualType () ? Object .class : mongoProperty .getFieldType ();
310
317
}
311
318
312
- private static boolean isCollection ( MongoPersistentProperty property ) {
313
- return property . isCollectionLike () && ! property . getType ().equals ( byte []. class );
319
+ private static Class <?> computeTargetType ( Class <?> fallback , List < Object > possibleValues ) {
320
+ return possibleValues . isEmpty () ? fallback : possibleValues . iterator ().next (). getClass ( );
314
321
}
315
322
316
- static JsonSchemaProperty createPotentiallyRequiredSchemaProperty ( JsonSchemaProperty property , boolean required ) {
323
+ private < E extends Enum < E >> List < Object > getPossibleEnumValues ( Class < E > targetType ) {
317
324
318
- if (!required ) {
319
- return property ;
325
+ EnumSet <E > enumSet = EnumSet .allOf (targetType );
326
+ List <Object > possibleValues = new ArrayList <>(enumSet .size ());
327
+
328
+ for (Object enumValue : enumSet ) {
329
+ possibleValues .add (converter .convertToMongoType (enumValue ));
320
330
}
321
331
322
- return JsonSchemaProperty .required (property );
332
+ return possibleValues ;
333
+ }
334
+
335
+ private static boolean isCollection (MongoPersistentProperty property ) {
336
+ return property .isCollectionLike () && !property .getType ().equals (byte [].class );
337
+ }
338
+
339
+ static JsonSchemaProperty createPotentiallyRequiredSchemaProperty (JsonSchemaProperty property , boolean required ) {
340
+ return required ? JsonSchemaProperty .required (property ) : property ;
323
341
}
324
342
325
343
class PropertyContext implements JsonSchemaPropertyContext {
326
344
327
- private String path ;
328
- private MongoPersistentProperty property ;
345
+ private final String path ;
346
+ private final MongoPersistentProperty property ;
329
347
330
348
public PropertyContext (String path , MongoPersistentProperty property ) {
331
349
this .path = path ;
0 commit comments