From ce9aae49c8661fd912c2d43eb9e5d4b803a133f3 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 27 Jun 2017 17:12:43 +0200 Subject: [PATCH] DATACMNS-1101 - Use non-capturing lambdas in CustomConversions for conversion lookup. --- .../data/convert/CustomConversions.java | 26 ++++++----- .../data/convert/DefaultTypeMapper.java | 43 ++++++++++++------- .../mapping/model/BasicPersistentEntity.java | 16 +++---- .../convert/CustomConversionsUnitTests.java | 4 +- 4 files changed, 54 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/springframework/data/convert/CustomConversions.java b/src/main/java/org/springframework/data/convert/CustomConversions.java index 376b98ae6..f1bbd2dc3 100644 --- a/src/main/java/org/springframework/data/convert/CustomConversions.java +++ b/src/main/java/org/springframework/data/convert/CustomConversions.java @@ -24,6 +24,7 @@ import lombok.extern.slf4j.Slf4j; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; import org.springframework.core.GenericTypeResolver; import org.springframework.core.convert.converter.Converter; @@ -69,8 +70,8 @@ public class CustomConversions { DEFAULT_CONVERTERS = Collections.unmodifiableList(defaults); } - private final Set readingPairs; - private final Set writingPairs; + private final Set readingPairs = new LinkedHashSet<>(); + private final Set writingPairs = new LinkedHashSet<>(); private final Set> customSimpleTypes; private final SimpleTypeHolder simpleTypeHolder; @@ -80,6 +81,15 @@ public class CustomConversions { private final Map>> customWriteTargetTypes; private final Map, Optional>> rawWriteTargetTypes; + private final Function>> getCustomReadTarget = it -> getCustomTarget( + it.getSourceType(), Optional.of(it.getTargetType()), readingPairs); + + private final Function, Optional>> getCustomWriteTargetForSource = it -> getCustomTarget(it, + Optional.empty(), writingPairs); + + private final Function>> getCustomWriteTarget = it -> getCustomTarget( + it.getSourceType(), Optional.of(it.getTargetType()), writingPairs); + /** * Creates a new {@link CustomConversions} instance registering the given converters. * @@ -91,8 +101,6 @@ public class CustomConversions { Assert.notNull(storeConversions, "StoreConversions must not be null!"); Assert.notNull(converters, "List of converters must not be null!"); - this.readingPairs = new LinkedHashSet<>(); - this.writingPairs = new LinkedHashSet<>(); this.customSimpleTypes = new HashSet<>(); this.customReadTargetTypes = new ConcurrentHashMap<>(); this.customWriteTargetTypes = new ConcurrentHashMap<>(); @@ -229,8 +237,7 @@ public class CustomConversions { Assert.notNull(sourceType, "Source type must not be null!"); - return rawWriteTargetTypes.computeIfAbsent(sourceType, - it -> getCustomTarget(sourceType, Optional.empty(), writingPairs)); + return rawWriteTargetTypes.computeIfAbsent(sourceType, getCustomWriteTargetForSource); } /** @@ -248,7 +255,7 @@ public class CustomConversions { Assert.notNull(requestedTargetType, "Target type must not be null!"); return customWriteTargetTypes.computeIfAbsent(new ConvertiblePair(sourceType, requestedTargetType), - it -> getCustomTarget(sourceType, Optional.of(requestedTargetType), writingPairs)); + getCustomWriteTarget); } /** @@ -307,8 +314,7 @@ public class CustomConversions { */ private Optional> getCustomReadTarget(Class sourceType, Class targetType) { - return customReadTargetTypes.computeIfAbsent(new ConvertiblePair(sourceType, targetType), - it -> getCustomTarget(sourceType, Optional.of(targetType), readingPairs)); + return customReadTargetTypes.computeIfAbsent(new ConvertiblePair(sourceType, targetType), getCustomReadTarget); } /** @@ -320,7 +326,7 @@ public class CustomConversions { * @param pairs must not be {@literal null}. * @return */ - private static Optional> getCustomTarget(Class sourceType, Optional> targetType, + private Optional> getCustomTarget(Class sourceType, Optional> targetType, Collection pairs) { Assert.notNull(sourceType, "Source Class must not be null!"); diff --git a/src/main/java/org/springframework/data/convert/DefaultTypeMapper.java b/src/main/java/org/springframework/data/convert/DefaultTypeMapper.java index f494b68d6..becd5b45a 100644 --- a/src/main/java/org/springframework/data/convert/DefaultTypeMapper.java +++ b/src/main/java/org/springframework/data/convert/DefaultTypeMapper.java @@ -21,12 +21,12 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; import org.springframework.data.mapping.Alias; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.util.ClassTypeInformation; -import org.springframework.data.util.Optionals; import org.springframework.data.util.TypeInformation; import org.springframework.util.Assert; @@ -43,6 +43,8 @@ public class DefaultTypeMapper implements TypeMapper { private final List mappers; private final Map>> typeCache; + private final Function>> getAlias; + /** * Creates a new {@link DefaultTypeMapper} using the given {@link TypeAliasAccessor}. It will use a * {@link SimpleTypeInformationMapper} to calculate type aliases. @@ -89,6 +91,17 @@ public class DefaultTypeMapper implements TypeMapper { this.mappers = Collections.unmodifiableList(mappers); this.accessor = accessor; this.typeCache = new ConcurrentHashMap<>(); + this.getAlias = key -> { + + for (TypeInformationMapper mapper : mappers) { + TypeInformation typeInformation = mapper.resolveTypeFrom(key); + + if (typeInformation != null) { + return Optional.of(typeInformation); + } + } + return Optional.empty(); + }; } /* @@ -110,18 +123,7 @@ public class DefaultTypeMapper implements TypeMapper { * @return */ private TypeInformation getFromCacheOrCreate(Alias alias) { - return typeCache.computeIfAbsent(alias, key -> { - - for (TypeInformationMapper mapper : mappers) { - TypeInformation typeInformation = mapper.resolveTypeFrom(alias); - - if (typeInformation != null) { - return Optional.of(typeInformation); - } - } - return Optional.empty(); - - }).orElse(null); + return typeCache.computeIfAbsent(alias, getAlias).orElse(null); } /* @@ -194,7 +196,10 @@ public class DefaultTypeMapper implements TypeMapper { Assert.notNull(info, "TypeInformation must not be null!"); - getAliasFor(info).getValue().ifPresent(it -> accessor.writeTypeTo(sink, it)); + Alias alias = getAliasFor(info); + if (alias.isPresent()) { + accessor.writeTypeTo(sink, alias.getValue().get()); + } } /** @@ -208,6 +213,14 @@ public class DefaultTypeMapper implements TypeMapper { Assert.notNull(info, "TypeInformation must not be null!"); - return Optionals.firstNonEmpty(mappers, it -> it.createAliasFor(info), Alias.NONE); + for (TypeInformationMapper mapper : mappers) { + + Alias alias = mapper.createAliasFor(info); + if (alias.isPresent()) { + return alias; + } + } + + return Alias.NONE; } } diff --git a/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java b/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java index 0d0710648..c80418fa5 100644 --- a/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java +++ b/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java @@ -30,8 +30,6 @@ import java.util.Optional; import java.util.Set; import java.util.TreeSet; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.data.annotation.TypeAlias; import org.springframework.data.mapping.*; @@ -52,9 +50,7 @@ import org.springframework.util.StringUtils; */ public class BasicPersistentEntity> implements MutablePersistentEntity { - private static final Logger LOGGER = LoggerFactory.getLogger(BasicPersistentEntity.class); private static final String TYPE_MISMATCH = "Target bean of type %s is not of type of the persistent entity (%s)!"; - private static final String NULL_ASSOCIATION = "%s.addAssociation(…) was called with a null association! Usually indicates a problem in a Spring Data MappingContext implementation. Be sure to file a bug at https://jira.spring.io!"; private final PreferredConstructor constructor; private final TypeInformation information; @@ -438,8 +434,10 @@ public class BasicPersistentEntity> implement public PersistentPropertyAccessor getPropertyAccessor(Object bean) { Assert.notNull(bean, "Target bean must not be null!"); - Assert.isTrue(getType().isInstance(bean), - () -> String.format(TYPE_MISMATCH, bean.getClass().getName(), getType().getName())); + + if (!getType().isInstance(bean)) { // prevent capturing lambda + throw new IllegalArgumentException(String.format(TYPE_MISMATCH, bean.getClass().getName(), getType().getName())); + } return propertyAccessorFactory.getPropertyAccessor(this, bean); } @@ -452,8 +450,10 @@ public class BasicPersistentEntity> implement public IdentifierAccessor getIdentifierAccessor(Object bean) { Assert.notNull(bean, "Target bean must not be null!"); - Assert.isTrue(getType().isInstance(bean), - () -> String.format(TYPE_MISMATCH, bean.getClass().getName(), getType().getName())); + + if (!getType().isInstance(bean)) { // prevent capturing lambda + throw new IllegalArgumentException(String.format(TYPE_MISMATCH, bean.getClass().getName(), getType().getName())); + } return hasIdProperty() ? new IdPropertyIdentifierAccessor(this, bean) : new AbsentIdentifierAccessor(bean); } diff --git a/src/test/java/org/springframework/data/convert/CustomConversionsUnitTests.java b/src/test/java/org/springframework/data/convert/CustomConversionsUnitTests.java index 8b349f492..6bb6788c0 100644 --- a/src/test/java/org/springframework/data/convert/CustomConversionsUnitTests.java +++ b/src/test/java/org/springframework/data/convert/CustomConversionsUnitTests.java @@ -40,8 +40,8 @@ import org.springframework.data.convert.CustomConversions.StoreConversions; import org.threeten.bp.LocalDateTime; /** - * Unit tests for {@link MongoCustomConversions}. - * + * Unit tests for {@link CustomConversions}. + * * @author Oliver Gierke * @author Christoph Strobl * @since 2.0