3838import org .bson .conversions .Bson ;
3939import org .bson .json .JsonReader ;
4040import org .bson .types .ObjectId ;
41-
4241import org .springframework .beans .BeansException ;
4342import org .springframework .beans .factory .BeanClassLoaderAware ;
4443import org .springframework .context .ApplicationContext ;
@@ -187,7 +186,7 @@ protected ConversionContext getConversionContext(ObjectPath path) {
187186
188187 Assert .notNull (path , "ObjectPath must not be null" );
189188
190- return new ConversionContext (this , conversions , path , this ::readDocument , this ::readCollectionOrArray ,
189+ return new DefaultConversionContext (this , conversions , path , this ::readDocument , this ::readCollectionOrArray ,
191190 this ::readMap , this ::readDBRef , this ::getPotentiallyConvertedSimpleRead );
192191 }
193192
@@ -396,7 +395,46 @@ private Object doReadOrProject(ConversionContext context, Bson source, TypeInfor
396395 return readDocument (context , source , typeHint );
397396 }
398397
399- class ProjectingConversionContext extends ConversionContext {
398+ static class AssociationConversionContext implements ConversionContext {
399+
400+ private final ConversionContext delegate ;
401+
402+ public AssociationConversionContext (ConversionContext delegate ) {
403+ this .delegate = delegate ;
404+ }
405+
406+ @ Override
407+ public <S > S convert (Object source , TypeInformation <? extends S > typeHint , ConversionContext context ) {
408+ return delegate .convert (source , typeHint , context );
409+ }
410+
411+ @ Override
412+ public ConversionContext withPath (ObjectPath currentPath ) {
413+ return new AssociationConversionContext (delegate .withPath (currentPath ));
414+ }
415+
416+ @ Override
417+ public ObjectPath getPath () {
418+ return delegate .getPath ();
419+ }
420+
421+ @ Override
422+ public CustomConversions getCustomConversions () {
423+ return delegate .getCustomConversions ();
424+ }
425+
426+ @ Override
427+ public MongoConverter getSourceConverter () {
428+ return delegate .getSourceConverter ();
429+ }
430+
431+ @ Override
432+ public boolean resolveIdsInContext () {
433+ return true ;
434+ }
435+ }
436+
437+ class ProjectingConversionContext extends DefaultConversionContext {
400438
401439 private final EntityProjection <?, ?> returnedTypeDescriptor ;
402440
@@ -412,20 +450,21 @@ class ProjectingConversionContext extends ConversionContext {
412450 }
413451
414452 @ Override
415- public ConversionContext forProperty (String name ) {
453+ public DefaultConversionContext forProperty (String name ) {
416454
417455 EntityProjection <?, ?> property = returnedTypeDescriptor .findProperty (name );
418456 if (property == null ) {
419- return new ConversionContext (sourceConverter , conversions , path , MappingMongoConverter .this ::readDocument ,
420- collectionConverter , mapConverter , dbRefConverter , elementConverter );
457+ return new DefaultConversionContext (sourceConverter , conversions , path ,
458+ MappingMongoConverter .this ::readDocument , collectionConverter , mapConverter , dbRefConverter ,
459+ elementConverter );
421460 }
422461
423462 return new ProjectingConversionContext (sourceConverter , conversions , path , collectionConverter , mapConverter ,
424463 dbRefConverter , elementConverter , property );
425464 }
426465
427466 @ Override
428- public ConversionContext withPath (ObjectPath currentPath ) {
467+ public DefaultConversionContext withPath (ObjectPath currentPath ) {
429468 return new ProjectingConversionContext (sourceConverter , conversions , currentPath , collectionConverter ,
430469 mapConverter , dbRefConverter , elementConverter , returnedTypeDescriptor );
431470 }
@@ -544,7 +583,7 @@ private <S> S read(ConversionContext context, MongoPersistentEntity<S> entity, D
544583 SpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator (bson , spELContext );
545584 DocumentAccessor documentAccessor = new DocumentAccessor (bson );
546585
547- if (hasIdentifier (bson )) {
586+ if (context . resolveIdsInContext () && hasIdentifier (bson )) {
548587 S existing = findContextualEntity (context , entity , bson );
549588 if (existing != null ) {
550589 return existing ;
@@ -647,7 +686,7 @@ private void readProperties(ConversionContext context, MongoPersistentEntity<?>
647686 continue ;
648687 }
649688
650- ConversionContext propertyContext = context .forProperty (prop . getName () );
689+ ConversionContext propertyContext = context .forProperty (prop );
651690 MongoDbPropertyValueProvider valueProviderToUse = valueProvider .withContext (propertyContext );
652691
653692 if (prop .isAssociation () && !entity .isConstructorArgument (prop )) {
@@ -721,7 +760,7 @@ private void readAssociation(Association<MongoPersistentProperty> association, P
721760 accessor .setProperty (property ,
722761 dbRefResolver .resolveReference (property ,
723762 new DocumentReferenceSource (documentAccessor .getDocument (), documentAccessor .get (property )),
724- referenceLookupDelegate , context ::convert ));
763+ referenceLookupDelegate , context . forProperty ( property ) ::convert ));
725764 }
726765 return ;
727766 }
@@ -1971,13 +2010,13 @@ public <T> T getPropertyValue(MongoPersistentProperty property) {
19712010 return null ;
19722011 }
19732012
1974- CustomConversions conversions = context .conversions ;
2013+ CustomConversions conversions = context .getCustomConversions () ;
19752014 if (conversions .getPropertyValueConversions ().hasValueConverter (property )) {
19762015 return (T ) conversions .getPropertyValueConversions ().getValueConverter (property ).read (value ,
1977- new MongoConversionContext (property , context .sourceConverter ));
2016+ new MongoConversionContext (property , context .getSourceConverter () ));
19782017 }
19792018
1980- ConversionContext contextToUse = context .forProperty (property . getName () );
2019+ ConversionContext contextToUse = context .forProperty (property );
19812020
19822021 return (T ) contextToUse .convert (value , property .getTypeInformation ());
19832022 }
@@ -2201,13 +2240,49 @@ public TypeDescriptor toTypeDescriptor() {
22012240 }
22022241 }
22032242
2243+ interface ConversionContext {
2244+
2245+ default <S extends Object > S convert (Object source , TypeInformation <? extends S > typeHint ) {
2246+ return convert (source , typeHint , this );
2247+ }
2248+
2249+ <S extends Object > S convert (Object source , TypeInformation <? extends S > typeHint , ConversionContext context );
2250+
2251+ ConversionContext withPath (ObjectPath currentPath );
2252+
2253+ ObjectPath getPath ();
2254+
2255+ default ConversionContext forProperty (String name ) {
2256+ return this ;
2257+ }
2258+
2259+ default ConversionContext forProperty (@ Nullable PersistentProperty property ) {
2260+
2261+ if (property != null ) {
2262+ if (property .isAssociation ()) {
2263+ return new AssociationConversionContext (forProperty (property .getName ()));
2264+ }
2265+ return forProperty (property .getName ());
2266+ }
2267+ return this ;
2268+ }
2269+
2270+ default boolean resolveIdsInContext () {
2271+ return false ;
2272+ }
2273+
2274+ CustomConversions getCustomConversions ();
2275+
2276+ MongoConverter getSourceConverter ();
2277+ }
2278+
22042279 /**
22052280 * Conversion context holding references to simple {@link ValueConverter} and {@link ContainerValueConverter}.
22062281 * Entrypoint for recursive conversion of {@link Document} and other types.
22072282 *
22082283 * @since 3.2
22092284 */
2210- protected static class ConversionContext {
2285+ protected static class DefaultConversionContext implements ConversionContext {
22112286
22122287 final MongoConverter sourceConverter ;
22132288 final org .springframework .data .convert .CustomConversions conversions ;
@@ -2218,7 +2293,7 @@ protected static class ConversionContext {
22182293 final ContainerValueConverter <DBRef > dbRefConverter ;
22192294 final ValueConverter <Object > elementConverter ;
22202295
2221- ConversionContext (MongoConverter sourceConverter ,
2296+ DefaultConversionContext (MongoConverter sourceConverter ,
22222297 org .springframework .data .convert .CustomConversions customConversions , ObjectPath path ,
22232298 ContainerValueConverter <Bson > documentConverter , ContainerValueConverter <Collection <?>> collectionConverter ,
22242299 ContainerValueConverter <Bson > mapConverter , ContainerValueConverter <DBRef > dbRefConverter ,
@@ -2242,7 +2317,8 @@ protected static class ConversionContext {
22422317 * @return the converted object.
22432318 */
22442319 @ SuppressWarnings ("unchecked" )
2245- public <S extends Object > S convert (Object source , TypeInformation <? extends S > typeHint ) {
2320+ public <S extends Object > S convert (Object source , TypeInformation <? extends S > typeHint ,
2321+ ConversionContext context ) {
22462322
22472323 Assert .notNull (source , "Source must not be null" );
22482324 Assert .notNull (typeHint , "TypeInformation must not be null" );
@@ -2262,26 +2338,26 @@ public <S extends Object> S convert(Object source, TypeInformation<? extends S>
22622338 }
22632339
22642340 if (typeHint .isCollectionLike () || typeHint .getType ().isAssignableFrom (Collection .class )) {
2265- return (S ) collectionConverter .convert (this , (Collection <?>) source , typeHint );
2341+ return (S ) collectionConverter .convert (context , (Collection <?>) source , typeHint );
22662342 }
22672343 }
22682344
22692345 if (typeHint .isMap ()) {
22702346
22712347 if (ClassUtils .isAssignable (Document .class , typeHint .getType ())) {
2272- return (S ) documentConverter .convert (this , BsonUtils .asBson (source ), typeHint );
2348+ return (S ) documentConverter .convert (context , BsonUtils .asBson (source ), typeHint );
22732349 }
22742350
22752351 if (BsonUtils .supportsBson (source )) {
2276- return (S ) mapConverter .convert (this , BsonUtils .asBson (source ), typeHint );
2352+ return (S ) mapConverter .convert (context , BsonUtils .asBson (source ), typeHint );
22772353 }
22782354
22792355 throw new IllegalArgumentException (
22802356 String .format ("Expected map like structure but found %s" , source .getClass ()));
22812357 }
22822358
22832359 if (source instanceof DBRef ) {
2284- return (S ) dbRefConverter .convert (this , (DBRef ) source , typeHint );
2360+ return (S ) dbRefConverter .convert (context , (DBRef ) source , typeHint );
22852361 }
22862362
22872363 if (source instanceof Collection ) {
@@ -2290,31 +2366,41 @@ public <S extends Object> S convert(Object source, TypeInformation<? extends S>
22902366 }
22912367
22922368 if (BsonUtils .supportsBson (source )) {
2293- return (S ) documentConverter .convert (this , BsonUtils .asBson (source ), typeHint );
2369+ return (S ) documentConverter .convert (context , BsonUtils .asBson (source ), typeHint );
22942370 }
22952371
22962372 return (S ) elementConverter .convert (source , typeHint );
22972373 }
22982374
2375+ @ Override
2376+ public CustomConversions getCustomConversions () {
2377+ return conversions ;
2378+ }
2379+
2380+ @ Override
2381+ public MongoConverter getSourceConverter () {
2382+ return sourceConverter ;
2383+ }
2384+
22992385 /**
2300- * Create a new {@link ConversionContext } with {@link ObjectPath currentPath} applied.
2386+ * Create a new {@link DefaultConversionContext } with {@link ObjectPath currentPath} applied.
23012387 *
23022388 * @param currentPath must not be {@literal null}.
2303- * @return a new {@link ConversionContext } with {@link ObjectPath currentPath} applied.
2389+ * @return a new {@link DefaultConversionContext } with {@link ObjectPath currentPath} applied.
23042390 */
2305- public ConversionContext withPath (ObjectPath currentPath ) {
2391+ public DefaultConversionContext withPath (ObjectPath currentPath ) {
23062392
23072393 Assert .notNull (currentPath , "ObjectPath must not be null" );
23082394
2309- return new ConversionContext (sourceConverter , conversions , currentPath , documentConverter , collectionConverter ,
2310- mapConverter , dbRefConverter , elementConverter );
2395+ return new DefaultConversionContext (sourceConverter , conversions , currentPath , documentConverter ,
2396+ collectionConverter , mapConverter , dbRefConverter , elementConverter );
23112397 }
23122398
23132399 public ObjectPath getPath () {
23142400 return path ;
23152401 }
23162402
2317- public ConversionContext forProperty (String name ) {
2403+ public DefaultConversionContext forProperty (String name ) {
23182404 return this ;
23192405 }
23202406
0 commit comments