Skip to content

Commit 5fc49b1

Browse files
committed
Polishing.
Encapsulate nested object lookup. Refine method signatures and tweak Javadoc. See #4098 Original pull request: #4133.
1 parent 1e7dc7c commit 5fc49b1

File tree

1 file changed

+150
-122
lines changed

1 file changed

+150
-122
lines changed

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java

+150-122
Original file line numberDiff line numberDiff line change
@@ -384,81 +384,6 @@ private Object doReadOrProject(ConversionContext context, Bson source, TypeInfor
384384
return readDocument(context, source, typeHint);
385385
}
386386

387-
static class AssociationConversionContext implements ConversionContext {
388-
389-
private final ConversionContext delegate;
390-
391-
public AssociationConversionContext(ConversionContext delegate) {
392-
this.delegate = delegate;
393-
}
394-
395-
@Override
396-
public <S> S convert(Object source, TypeInformation<? extends S> typeHint, ConversionContext context) {
397-
return delegate.convert(source, typeHint, context);
398-
}
399-
400-
@Override
401-
public ConversionContext withPath(ObjectPath currentPath) {
402-
return new AssociationConversionContext(delegate.withPath(currentPath));
403-
}
404-
405-
@Override
406-
public ObjectPath getPath() {
407-
return delegate.getPath();
408-
}
409-
410-
@Override
411-
public CustomConversions getCustomConversions() {
412-
return delegate.getCustomConversions();
413-
}
414-
415-
@Override
416-
public MongoConverter getSourceConverter() {
417-
return delegate.getSourceConverter();
418-
}
419-
420-
@Override
421-
public boolean resolveIdsInContext() {
422-
return true;
423-
}
424-
}
425-
426-
class ProjectingConversionContext extends DefaultConversionContext {
427-
428-
private final EntityProjection<?, ?> returnedTypeDescriptor;
429-
430-
ProjectingConversionContext(MongoConverter sourceConverter, CustomConversions customConversions, ObjectPath path,
431-
ContainerValueConverter<Collection<?>> collectionConverter, ContainerValueConverter<Bson> mapConverter,
432-
ContainerValueConverter<DBRef> dbRefConverter, ValueConverter<Object> elementConverter,
433-
EntityProjection<?, ?> projection) {
434-
super(sourceConverter, customConversions, path,
435-
(context, source, typeHint) -> doReadOrProject(context, source, typeHint, projection),
436-
437-
collectionConverter, mapConverter, dbRefConverter, elementConverter);
438-
this.returnedTypeDescriptor = projection;
439-
}
440-
441-
@Override
442-
public DefaultConversionContext forProperty(String name) {
443-
444-
EntityProjection<?, ?> property = returnedTypeDescriptor.findProperty(name);
445-
if (property == null) {
446-
return new DefaultConversionContext(sourceConverter, conversions, path,
447-
MappingMongoConverter.this::readDocument, collectionConverter, mapConverter, dbRefConverter,
448-
elementConverter);
449-
}
450-
451-
return new ProjectingConversionContext(sourceConverter, conversions, path, collectionConverter, mapConverter,
452-
dbRefConverter, elementConverter, property);
453-
}
454-
455-
@Override
456-
public DefaultConversionContext withPath(ObjectPath currentPath) {
457-
return new ProjectingConversionContext(sourceConverter, conversions, currentPath, collectionConverter,
458-
mapConverter, dbRefConverter, elementConverter, returnedTypeDescriptor);
459-
}
460-
}
461-
462387
static class MapPersistentPropertyAccessor implements PersistentPropertyAccessor<Map<String, Object>> {
463388

464389
Map<String, Object> map = new LinkedHashMap<>();
@@ -565,16 +490,14 @@ private ParameterValueProvider<MongoPersistentProperty> getParameterProvider(Con
565490

566491
private <S> S read(ConversionContext context, MongoPersistentEntity<S> entity, Document bson) {
567492

493+
S existing = context.findContextualEntity(entity, bson);
494+
if (existing != null) {
495+
return existing;
496+
}
497+
568498
SpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(bson, spELContext);
569499
DocumentAccessor documentAccessor = new DocumentAccessor(bson);
570500

571-
if (context.resolveIdsInContext() && hasIdentifier(bson)) {
572-
S existing = findContextualEntity(context, entity, bson);
573-
if (existing != null) {
574-
return existing;
575-
}
576-
}
577-
578501
PreferredConstructor<S, MongoPersistentProperty> persistenceConstructor = entity.getPersistenceConstructor();
579502

580503
ParameterValueProvider<MongoPersistentProperty> provider = persistenceConstructor != null
@@ -592,16 +515,6 @@ private <S> S read(ConversionContext context, MongoPersistentEntity<S> entity, D
592515
return instance;
593516
}
594517

595-
private boolean hasIdentifier(Document bson) {
596-
return bson.get(BasicMongoPersistentProperty.ID_FIELD_NAME) != null;
597-
}
598-
599-
@Nullable
600-
private <S> S findContextualEntity(ConversionContext context, MongoPersistentEntity<S> entity, Document bson) {
601-
return context.getPath().getPathItem(bson.get(BasicMongoPersistentProperty.ID_FIELD_NAME), entity.getCollection(),
602-
entity.getType());
603-
}
604-
605518
private <S> S populateProperties(ConversionContext context, MongoPersistentEntity<S> entity,
606519
DocumentAccessor documentAccessor, SpELExpressionEvaluator evaluator, S instance) {
607520

@@ -2197,40 +2110,130 @@ public TypeDescriptor toTypeDescriptor() {
21972110
}
21982111
}
21992112

2113+
/**
2114+
* Conversion context defining an interface for graph-traversal-based conversion of documents. Entrypoint for
2115+
* recursive conversion of {@link Document} and other types.
2116+
*
2117+
* @since 3.4.3
2118+
*/
22002119
interface ConversionContext {
22012120

2121+
/**
2122+
* Converts a source object into {@link TypeInformation target}.
2123+
*
2124+
* @param source must not be {@literal null}.
2125+
* @param typeHint must not be {@literal null}.
2126+
* @return the converted object.
2127+
*/
22022128
default <S extends Object> S convert(Object source, TypeInformation<? extends S> typeHint) {
22032129
return convert(source, typeHint, this);
22042130
}
22052131

2132+
/**
2133+
* Converts a source object into {@link TypeInformation target}.
2134+
*
2135+
* @param source must not be {@literal null}.
2136+
* @param typeHint must not be {@literal null}.
2137+
* @param context must not be {@literal null}.
2138+
* @return the converted object.
2139+
*/
22062140
<S extends Object> S convert(Object source, TypeInformation<? extends S> typeHint, ConversionContext context);
22072141

2142+
/**
2143+
* Create a new {@link ConversionContext} with {@link ObjectPath currentPath} applied.
2144+
*
2145+
* @param currentPath must not be {@literal null}.
2146+
* @return a new {@link ConversionContext} with {@link ObjectPath currentPath} applied.
2147+
*/
22082148
ConversionContext withPath(ObjectPath currentPath);
22092149

2210-
ObjectPath getPath();
2211-
2150+
/**
2151+
* Obtain a {@link ConversionContext} for the given property {@code name}.
2152+
*
2153+
* @param name must not be {@literal null}.
2154+
* @return the {@link ConversionContext} to be used for conversion of the given property.
2155+
*/
22122156
default ConversionContext forProperty(String name) {
22132157
return this;
22142158
}
22152159

2216-
default ConversionContext forProperty(@Nullable PersistentProperty property) {
2160+
/**
2161+
* Obtain a {@link ConversionContext} for the given {@link MongoPersistentProperty}.
2162+
*
2163+
* @param property must not be {@literal null}.
2164+
* @return the {@link ConversionContext} to be used for conversion of the given property.
2165+
*/
2166+
default ConversionContext forProperty(MongoPersistentProperty property) {
22172167

2218-
if (property != null) {
2219-
if (property.isAssociation()) {
2220-
return new AssociationConversionContext(forProperty(property.getName()));
2221-
}
2222-
return forProperty(property.getName());
2223-
}
2224-
return this;
2168+
return property.isAssociation() ? new AssociationConversionContext(forProperty(property.getName()))
2169+
: forProperty(property.getName());
22252170
}
22262171

2227-
default boolean resolveIdsInContext() {
2228-
return false;
2172+
/**
2173+
* Lookup a potentially existing entity instance of the given {@link MongoPersistentEntity} and {@link Document}
2174+
*
2175+
* @param entity
2176+
* @param document
2177+
* @return
2178+
* @param <S>
2179+
*/
2180+
@Nullable
2181+
default <S> S findContextualEntity(MongoPersistentEntity<S> entity, Document document) {
2182+
return null;
22292183
}
22302184

2185+
ObjectPath getPath();
2186+
22312187
CustomConversions getCustomConversions();
22322188

22332189
MongoConverter getSourceConverter();
2190+
2191+
}
2192+
2193+
/**
2194+
* @since 3.4.3
2195+
*/
2196+
static class AssociationConversionContext implements ConversionContext {
2197+
2198+
private final ConversionContext delegate;
2199+
2200+
public AssociationConversionContext(ConversionContext delegate) {
2201+
this.delegate = delegate;
2202+
}
2203+
2204+
@Override
2205+
public <S> S convert(Object source, TypeInformation<? extends S> typeHint, ConversionContext context) {
2206+
return delegate.convert(source, typeHint, context);
2207+
}
2208+
2209+
@Override
2210+
public ConversionContext withPath(ObjectPath currentPath) {
2211+
return new AssociationConversionContext(delegate.withPath(currentPath));
2212+
}
2213+
2214+
@Override
2215+
public <S> S findContextualEntity(MongoPersistentEntity<S> entity, Document document) {
2216+
2217+
Object identifier = document.get(BasicMongoPersistentProperty.ID_FIELD_NAME);
2218+
2219+
return identifier != null ? getPath().getPathItem(identifier, entity.getCollection(), entity.getType()) : null;
2220+
}
2221+
2222+
@Override
2223+
public ObjectPath getPath() {
2224+
return delegate.getPath();
2225+
}
2226+
2227+
@Override
2228+
public CustomConversions getCustomConversions() {
2229+
return delegate.getCustomConversions();
2230+
}
2231+
2232+
@Override
2233+
public MongoConverter getSourceConverter() {
2234+
return delegate.getSourceConverter();
2235+
}
2236+
22342237
}
22352238

22362239
/**
@@ -2266,14 +2269,8 @@ protected static class DefaultConversionContext implements ConversionContext {
22662269
this.elementConverter = elementConverter;
22672270
}
22682271

2269-
/**
2270-
* Converts a source object into {@link TypeInformation target}.
2271-
*
2272-
* @param source must not be {@literal null}.
2273-
* @param typeHint must not be {@literal null}.
2274-
* @return the converted object.
2275-
*/
22762272
@SuppressWarnings("unchecked")
2273+
@Override
22772274
public <S extends Object> S convert(Object source, TypeInformation<? extends S> typeHint,
22782275
ConversionContext context) {
22792276

@@ -2339,28 +2336,20 @@ public MongoConverter getSourceConverter() {
23392336
return sourceConverter;
23402337
}
23412338

2342-
/**
2343-
* Create a new {@link DefaultConversionContext} with {@link ObjectPath currentPath} applied.
2344-
*
2345-
* @param currentPath must not be {@literal null}.
2346-
* @return a new {@link DefaultConversionContext} with {@link ObjectPath currentPath} applied.
2347-
*/
2348-
public DefaultConversionContext withPath(ObjectPath currentPath) {
2339+
@Override
2340+
public ConversionContext withPath(ObjectPath currentPath) {
23492341

23502342
Assert.notNull(currentPath, "ObjectPath must not be null");
23512343

23522344
return new DefaultConversionContext(sourceConverter, conversions, currentPath, documentConverter,
23532345
collectionConverter, mapConverter, dbRefConverter, elementConverter);
23542346
}
23552347

2348+
@Override
23562349
public ObjectPath getPath() {
23572350
return path;
23582351
}
23592352

2360-
public DefaultConversionContext forProperty(String name) {
2361-
return this;
2362-
}
2363-
23642353
/**
23652354
* Converts a simple {@code source} value into {@link TypeInformation the target type}.
23662355
*
@@ -2386,6 +2375,45 @@ interface ContainerValueConverter<T> {
23862375

23872376
}
23882377

2378+
/**
2379+
* @since 3.4.3
2380+
*/
2381+
class ProjectingConversionContext extends DefaultConversionContext {
2382+
2383+
private final EntityProjection<?, ?> returnedTypeDescriptor;
2384+
2385+
ProjectingConversionContext(MongoConverter sourceConverter, CustomConversions customConversions, ObjectPath path,
2386+
ContainerValueConverter<Collection<?>> collectionConverter, ContainerValueConverter<Bson> mapConverter,
2387+
ContainerValueConverter<DBRef> dbRefConverter, ValueConverter<Object> elementConverter,
2388+
EntityProjection<?, ?> projection) {
2389+
super(sourceConverter, customConversions, path,
2390+
(context, source, typeHint) -> doReadOrProject(context, source, typeHint, projection),
2391+
2392+
collectionConverter, mapConverter, dbRefConverter, elementConverter);
2393+
this.returnedTypeDescriptor = projection;
2394+
}
2395+
2396+
@Override
2397+
public ConversionContext forProperty(String name) {
2398+
2399+
EntityProjection<?, ?> property = returnedTypeDescriptor.findProperty(name);
2400+
if (property == null) {
2401+
return new DefaultConversionContext(sourceConverter, conversions, path,
2402+
MappingMongoConverter.this::readDocument, collectionConverter, mapConverter, dbRefConverter,
2403+
elementConverter);
2404+
}
2405+
2406+
return new ProjectingConversionContext(sourceConverter, conversions, path, collectionConverter, mapConverter,
2407+
dbRefConverter, elementConverter, property);
2408+
}
2409+
2410+
@Override
2411+
public ConversionContext withPath(ObjectPath currentPath) {
2412+
return new ProjectingConversionContext(sourceConverter, conversions, currentPath, collectionConverter,
2413+
mapConverter, dbRefConverter, elementConverter, returnedTypeDescriptor);
2414+
}
2415+
}
2416+
23892417
private static class PropertyTranslatingPropertyAccessor<T> implements PersistentPropertyPathAccessor<T> {
23902418

23912419
private final PersistentPropertyAccessor<T> delegate;

0 commit comments

Comments
 (0)