2424import java .util .stream .Collectors ;
2525
2626import org .bson .Document ;
27+
2728import org .springframework .data .mapping .PersistentProperty ;
2829import org .springframework .data .mapping .context .MappingContext ;
2930import org .springframework .data .mongodb .core .convert .MongoConverter ;
4041import org .springframework .data .mongodb .core .schema .MongoJsonSchema ;
4142import org .springframework .data .mongodb .core .schema .MongoJsonSchema .MongoJsonSchemaBuilder ;
4243import org .springframework .data .mongodb .core .schema .TypedJsonSchemaObject ;
43- import org .springframework .lang . Nullable ;
44+ import org .springframework .data . util . ClassTypeInformation ;
4445import org .springframework .util .Assert ;
4546import org .springframework .util .ClassUtils ;
4647import org .springframework .util .CollectionUtils ;
@@ -168,7 +169,7 @@ private JsonSchemaProperty computeSchemaForProperty(List<MongoPersistentProperty
168169
169170 JsonSchemaProperty schemaProperty ;
170171 if (isCollection (property )) {
171- schemaProperty = createSchemaPropertyForCollection (fieldName , property , required );
172+ schemaProperty = createArraySchemaProperty (fieldName , property , required );
172173 } else if (property .isMap ()) {
173174 schemaProperty = createSchemaProperty (fieldName , Type .objectType (), required );
174175 } else if (ClassUtils .isAssignable (Enum .class , targetType )) {
@@ -180,49 +181,52 @@ private JsonSchemaProperty computeSchemaForProperty(List<MongoPersistentProperty
180181 return applyEncryptionDataIfNecessary (property , schemaProperty );
181182 }
182183
183- private JsonSchemaProperty createSchemaPropertyForCollection (String fieldName , MongoPersistentProperty property ,
184+ private JsonSchemaProperty createArraySchemaProperty (String fieldName , MongoPersistentProperty property ,
184185 boolean required ) {
185186
186187 ArrayJsonSchemaProperty schemaProperty = JsonSchemaProperty .array (fieldName );
187188
188- if (property .getActualType () != Object .class ) {
189+ if (isSpecificType (property )) {
190+ schemaProperty = potentiallyEnhanceArraySchemaProperty (property , schemaProperty );
191+ }
189192
190- MongoPersistentEntity <?> persistentEntity = mappingContext
191- . getPersistentEntity ( property . getTypeInformation (). getComponentType ());
193+ return createPotentiallyRequiredSchemaProperty ( schemaProperty , required );
194+ }
192195
193- if (persistentEntity == null ) {
196+ @ SuppressWarnings ({ "unchecked" , "rawtypes" })
197+ private ArrayJsonSchemaProperty potentiallyEnhanceArraySchemaProperty (MongoPersistentProperty property ,
198+ ArrayJsonSchemaProperty schemaProperty ) {
194199
195- if (ClassUtils .isAssignable (Enum .class , property .getActualType ())) {
200+ MongoPersistentEntity <?> persistentEntity = mappingContext
201+ .getPersistentEntity (property .getTypeInformation ().getRequiredComponentType ());
196202
197- List < Object > possibleValues = new ArrayList <>();
203+ if ( persistentEntity != null ) {
198204
199- for (Object enumValue : EnumSet .allOf ((Class ) property .getActualType ())) {
200- possibleValues .add (converter .convertToMongoType (enumValue ));
201- }
205+ List <JsonSchemaProperty > nestedProperties = computePropertiesForEntity (Collections .emptyList (), persistentEntity );
202206
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+ }
211210
212- List <JsonSchemaProperty > nestedProperties = computePropertiesForEntity (Collections .emptyList (),
213- persistentEntity );
211+ return schemaProperty
212+ .items (JsonSchemaObject .object ().properties (nestedProperties .toArray (new JsonSchemaProperty [0 ])));
213+ }
214214
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 ));
220221 }
221222
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 ());
223228 }
224229
225- @ Nullable
226230 private JsonSchemaProperty applyEncryptionDataIfNecessary (MongoPersistentProperty property ,
227231 JsonSchemaProperty schemaProperty ) {
228232
@@ -252,15 +256,12 @@ private JsonSchemaProperty createObjectSchemaPropertyForEntity(List<MongoPersist
252256 target .properties (nestedProperties .toArray (new JsonSchemaProperty [0 ])), required );
253257 }
254258
259+ @ SuppressWarnings ({ "unchecked" , "rawtypes" })
255260 private JsonSchemaProperty createEnumSchemaProperty (String fieldName , Class <?> targetType , boolean required ) {
256261
257- List <Object > possibleValues = new ArrayList <>( );
262+ List <Object > possibleValues = getPossibleEnumValues (( Class < Enum >) targetType );
258263
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 );
264265 return createSchemaProperty (fieldName , targetType , required , possibleValues );
265266 }
266267
@@ -271,14 +272,20 @@ JsonSchemaProperty createSchemaProperty(String fieldName, Object type, boolean r
271272 JsonSchemaProperty createSchemaProperty (String fieldName , Object type , boolean required ,
272273 Collection <?> possibleValues ) {
273274
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+
274282 TypedJsonSchemaObject schemaObject = type instanceof Type ? JsonSchemaObject .of (Type .class .cast (type ))
275283 : JsonSchemaObject .of (Class .class .cast (type ));
276284
277285 if (!CollectionUtils .isEmpty (possibleValues )) {
278286 schemaObject = schemaObject .possibleValues (possibleValues );
279287 }
280-
281- return createPotentiallyRequiredSchemaProperty (JsonSchemaProperty .named (fieldName ).with (schemaObject ), required );
288+ return schemaObject ;
282289 }
283290
284291 private String computePropertyFieldName (PersistentProperty property ) {
@@ -309,23 +316,34 @@ private Class<?> computeTargetType(PersistentProperty<?> property) {
309316 return mongoProperty .getFieldType () != mongoProperty .getActualType () ? Object .class : mongoProperty .getFieldType ();
310317 }
311318
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 ( );
314321 }
315322
316- static JsonSchemaProperty createPotentiallyRequiredSchemaProperty ( JsonSchemaProperty property , boolean required ) {
323+ private < E extends Enum < E >> List < Object > getPossibleEnumValues ( Class < E > targetType ) {
317324
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 ));
320330 }
321331
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 ;
323341 }
324342
325343 class PropertyContext implements JsonSchemaPropertyContext {
326344
327- private String path ;
328- private MongoPersistentProperty property ;
345+ private final String path ;
346+ private final MongoPersistentProperty property ;
329347
330348 public PropertyContext (String path , MongoPersistentProperty property ) {
331349 this .path = path ;
0 commit comments