diff --git a/src/main/java/org/springframework/data/mapping/PersistentPropertyAccessor.java b/src/main/java/org/springframework/data/mapping/PersistentPropertyAccessor.java index 7e4629883..88170feb1 100644 --- a/src/main/java/org/springframework/data/mapping/PersistentPropertyAccessor.java +++ b/src/main/java/org/springframework/data/mapping/PersistentPropertyAccessor.java @@ -17,7 +17,6 @@ package org.springframework.data.mapping; import org.springframework.data.mapping.model.ConvertingPropertyAccessor; import org.springframework.lang.Nullable; -import org.springframework.util.Assert; /** * Domain service to allow accessing and setting {@link PersistentProperty}s of an entity. Usually obtained through @@ -46,63 +45,6 @@ public interface PersistentPropertyAccessor { */ void setProperty(PersistentProperty property, @Nullable Object value); - /** - * Sets the given value for the {@link PersistentProperty} pointed to by the given {@link PersistentPropertyPath}. The - * lookup of intermediate values must not yield {@literal null}. - * - * @param path must not be {@literal null} or empty. - * @param value can be {@literal null}. - * @since 2.1 - * @deprecated since 2.3, use {@link PersistentPropertyPathAccessor#setProperty(PersistentPropertyPath, Object)} - * instead. - */ - @Deprecated - default void setProperty(PersistentPropertyPath> path, @Nullable Object value) { - - Assert.notNull(path, "PersistentPropertyPath must not be null"); - Assert.isTrue(!path.isEmpty(), "PersistentPropertyPath must not be empty"); - - PersistentProperty> leafProperty = path.getLeafProperty(); - PersistentPropertyPath> parentPath = path.getParentPath(); - - if (parentPath != null) { - - PersistentProperty> parentProperty = parentPath.getLeafProperty(); - - if (parentProperty.isCollectionLike() || parentProperty.isMap()) { - throw new MappingException( - "Cannot traverse collection or map intermediate %s".formatted(parentPath.toDotPath())); - } - } - - Object parent = parentPath == null ? getBean() : getProperty(parentPath); - - if (parent == null) { - - String nullIntermediateMessage = "Cannot lookup property %s on null intermediate; Original path was: %s on %s"; - - throw new MappingException( - String.format(nullIntermediateMessage, path.getParentPath(), path.toDotPath(), - getBean().getClass().getName())); - } - - PersistentPropertyAccessor accessor = parent == getBean() // - ? this // - : leafProperty.getOwner().getPropertyAccessor(parent); - - accessor.setProperty(leafProperty, value); - - if (parentPath == null) { - return; - } - - Object bean = accessor.getBean(); - - if (bean != parent) { - setProperty(parentPath, bean); - } - } - /** * Returns the value of the given {@link PersistentProperty} of the underlying bean instance. * @@ -112,65 +54,6 @@ public interface PersistentPropertyAccessor { @Nullable Object getProperty(PersistentProperty property); - /** - * Return the value pointed to by the given {@link PersistentPropertyPath}. If the given path is empty, the wrapped - * bean is returned. - * - * @param path must not be {@literal null}. - * @return - * @since 2.1 - * @deprecated since 2.3, use {@link PersistentPropertyPathAccessor#getProperty(PersistentPropertyPath)} instead - */ - @Deprecated - @Nullable - default Object getProperty(PersistentPropertyPath> path) { - return getProperty(path, new TraversalContext()); - } - - /** - * Return the value pointed to by the given {@link PersistentPropertyPath}. If the given path is empty, the wrapped - * bean is returned. On each path segment value lookup, the resulting value is post-processed by handlers registered - * on the given {@link TraversalContext} context. This can be used to unwrap container types that are encountered - * during the traversal. - * - * @param path must not be {@literal null}. - * @param context must not be {@literal null}. - * @return - * @since 2.2 - * @deprecated since 2.3, use - * {@link PersistentPropertyPathAccessor#getProperty(PersistentPropertyPath, org.springframework.data.mapping.AccessOptions.GetOptions)} - * instead. - */ - @Nullable - @Deprecated - default Object getProperty(PersistentPropertyPath> path, TraversalContext context) { - - Object bean = getBean(); - Object current = bean; - - if (path.isEmpty()) { - return bean; - } - - for (PersistentProperty property : path) { - - if (current == null) { - - String nullIntermediateMessage = "Cannot lookup property %s on null intermediate; Original path was: %s on %s"; - - throw new MappingException( - String.format(nullIntermediateMessage, property, path.toDotPath(), bean.getClass().getName())); - } - - PersistentEntity> entity = property.getOwner(); - PersistentPropertyAccessor accessor = entity.getPropertyAccessor(current); - - current = context.postProcess(property, accessor.getProperty(property)); - } - - return current; - } - /** * Returns the underlying bean. The actual instance may change between * {@link #setProperty(PersistentProperty, Object)} calls. diff --git a/src/main/java/org/springframework/data/mapping/PersistentPropertyPathAccessor.java b/src/main/java/org/springframework/data/mapping/PersistentPropertyPathAccessor.java index 289232404..0325b5890 100644 --- a/src/main/java/org/springframework/data/mapping/PersistentPropertyPathAccessor.java +++ b/src/main/java/org/springframework/data/mapping/PersistentPropertyPathAccessor.java @@ -37,10 +37,7 @@ public interface PersistentPropertyPathAccessor extends PersistentPropertyAcc * @return */ @Nullable - @SuppressWarnings("deprecation") - default Object getProperty(PersistentPropertyPath> path) { - return PersistentPropertyAccessor.super.getProperty(path); - } + Object getProperty(PersistentPropertyPath> path); /** * Return the value pointed to by the given {@link PersistentPropertyPath}. If the given path is empty, the wrapped diff --git a/src/main/java/org/springframework/data/mapping/TraversalContext.java b/src/main/java/org/springframework/data/mapping/TraversalContext.java deleted file mode 100644 index ad542b427..000000000 --- a/src/main/java/org/springframework/data/mapping/TraversalContext.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2019-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.mapping; - -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; - -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; - -/** - * A context object for lookups of values for {@link PersistentPropertyPaths} via a {@link PersistentPropertyAccessor}. - * It allows to register functions to post-process the objects returned for a particular property, so that the - * subsequent traversal would rather use the processed object. This is especially helpful if you need to traverse paths - * that contain {@link Collection}s and {@link Map} that usually need indices and keys to reasonably traverse nested - * properties. - * - * @author Oliver Drotbohm - * @since 2.2 - * @soundtrack 8-Bit Misfits - Crash Into Me (Dave Matthews Band cover) - */ -public class TraversalContext { - - private Map, Function> handlers = new HashMap<>(); - - /** - * Registers a {@link Function} to post-process values for the given property. - * - * @param property must not be {@literal null}. - * @param handler must not be {@literal null}. - * @return - */ - public TraversalContext registerHandler(PersistentProperty property, Function handler) { - - Assert.notNull(property, "Property must not be null"); - Assert.notNull(handler, "Handler must not be null"); - - handlers.put(property, handler); - - return this; - } - - /** - * Registers a {@link Function} to handle {@link Collection} values for the given property. - * - * @param property must not be {@literal null}. - * @param handler must not be {@literal null}. - * @return - */ - @SuppressWarnings("unchecked") - public TraversalContext registerCollectionHandler(PersistentProperty property, - Function, Object> handler) { - return registerHandler(property, Collection.class, (Function) handler); - } - - /** - * Registers a {@link Function} to handle {@link List} values for the given property. - * - * @param property must not be {@literal null}. - * @param handler must not be {@literal null}. - * @return - */ - @SuppressWarnings("unchecked") - public TraversalContext registerListHandler(PersistentProperty property, - Function, Object> handler) { - return registerHandler(property, List.class, (Function) handler); - } - - /** - * Registers a {@link Function} to handle {@link Set} values for the given property. - * - * @param property must not be {@literal null}. - * @param handler must not be {@literal null}. - * @return - */ - @SuppressWarnings("unchecked") - public TraversalContext registerSetHandler(PersistentProperty property, Function, Object> handler) { - return registerHandler(property, Set.class, (Function) handler); - } - - /** - * Registers a {@link Function} to handle {@link Map} values for the given property. - * - * @param property must not be {@literal null}. - * @param handler must not be {@literal null}. - * @return - */ - @SuppressWarnings("unchecked") - public TraversalContext registerMapHandler(PersistentProperty property, - Function, Object> handler) { - return registerHandler(property, Map.class, (Function) handler); - } - - /** - * Registers the given {@link Function} to post-process values obtained for the given {@link PersistentProperty} for - * the given type. - * - * @param the type of the value to handle. - * @param property must not be {@literal null}. - * @param type must not be {@literal null}. - * @param handler must not be {@literal null}. - * @return - */ - public TraversalContext registerHandler(PersistentProperty property, Class type, - Function handler) { - - Assert.isTrue(type.isAssignableFrom(property.getType()), () -> String - .format("Cannot register a property handler for %s on a property of type %s", type, property.getType())); - - Function caster = (Function) it -> type.cast(it); - - return registerHandler(property, caster.andThen(handler)); - } - - /** - * Post-processes the value obtained for the given {@link PersistentProperty} using the registered handler. - * - * @param property must not be {@literal null}. - * @param value can be {@literal null}. - * @return the post-processed value or the value itself if no handlers registered. - */ - @Nullable - Object postProcess(PersistentProperty property, @Nullable Object value) { - - Function handler = handlers.get(property); - - return handler == null ? value : handler.apply(value); - } -} diff --git a/src/test/java/org/springframework/data/mapping/PersistentPropertyAccessorUnitTests.java b/src/test/java/org/springframework/data/mapping/PersistentPropertyAccessorUnitTests.java index 0721b3b8c..f08e5d930 100644 --- a/src/test/java/org/springframework/data/mapping/PersistentPropertyAccessorUnitTests.java +++ b/src/test/java/org/springframework/data/mapping/PersistentPropertyAccessorUnitTests.java @@ -19,24 +19,13 @@ import static org.assertj.core.api.Assertions.*; import lombok.AccessLevel; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.Value; import lombok.With; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.stream.Stream; - import org.junit.jupiter.api.Test; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.data.mapping.context.SampleMappingContext; -import org.springframework.data.mapping.context.SamplePersistentProperty; import org.springframework.data.mapping.model.ConvertingPropertyAccessor; /** @@ -58,68 +47,6 @@ public class PersistentPropertyAccessorUnitTests { this.path = context.getPersistentPropertyPath(path, Order.class); } - @Test // DATACMNS-1275 - public void looksUpValueForPropertyPath() { - - var order = new Order(new Customer("Dave")); - - setUp(order, "customer.firstname"); - - assertThat(accessor.getProperty(path)).isEqualTo("Dave"); - } - - @Test // DATACMNS-1275 - public void setsPropertyOnNestedPath() { - - var customer = new Customer("Dave"); - var order = new Order(customer); - - setUp(order, "customer.firstname"); - - accessor.setProperty(path, "Oliver August"); - - assertThat(customer.firstname).isEqualTo("Oliver August"); - } - - @Test // DATACMNS-1275 - public void rejectsIntermediateNullValuesForRead() { - - setUp(new Order(null), "customer.firstname"); - - assertThatExceptionOfType(MappingException.class)// - .isThrownBy(() -> accessor.getProperty(path)); - } - - @Test // DATACMNS-1275 - public void rejectsIntermediateNullValuesForWrite() { - - setUp(new Order(null), "customer.firstname"); - - assertThatExceptionOfType(MappingException.class)// - .isThrownBy(() -> accessor.setProperty(path, "Oliver August")); - } - - @Test // DATACMNS-1322 - public void correctlyReplacesObjectInstancesWhenSettingPropertyPathOnImmutableObjects() { - - PersistentEntity entity = context.getPersistentEntity(Outer.class); - var path = context.getPersistentPropertyPath("immutable.value", - entity.getType()); - - var immutable = new NestedImmutable("foo"); - var outer = new Outer(immutable); - - PersistentPropertyAccessor accessor = entity.getPropertyAccessor(outer); - accessor.setProperty(path, "bar"); - - var result = accessor.getBean(); - - assertThat(result).isInstanceOfSatisfying(Outer.class, it -> { - assertThat(it.immutable).isNotSameAs(immutable); - assertThat(it).isNotSameAs(outer); - }); - } - @Test // DATACMNS-1377 public void shouldConvertToPropertyPathLeafType() { @@ -137,53 +64,6 @@ public class PersistentPropertyAccessorUnitTests { assertThat(convertingAccessor.getBean().getCustomer().getFirstname()).isEqualTo("2"); } - @Test // DATACMNS-1555 - public void usesTraversalContextToTraverseCollections() { - - var withContext = WithContext.builder() // - .collection(Collections.singleton("value")) // - .list(Collections.singletonList("value")) // - .set(Collections.singleton("value")) // - .map(Collections.singletonMap("key", "value")) // - .string(" value ") // - .build(); - - var collectionHelper = Spec.of("collection", - (context, property) -> context.registerCollectionHandler(property, it -> it.iterator().next())); - var listHelper = Spec.of("list", (context, property) -> context.registerListHandler(property, it -> it.get(0))); - var setHelper = Spec.of("set", - (context, property) -> context.registerSetHandler(property, it -> it.iterator().next())); - var mapHelper = Spec.of("map", (context, property) -> context.registerMapHandler(property, it -> it.get("key"))); - var stringHelper = Spec.of("string", - (context, property) -> context.registerHandler(property, String.class, it -> it.trim())); - - Stream.of(collectionHelper, listHelper, setHelper, mapHelper, stringHelper).forEach(it -> { - - PersistentEntity entity = context.getPersistentEntity(WithContext.class); - PersistentProperty property = entity.getRequiredPersistentProperty(it.name); - var accessor = entity.getPropertyAccessor(withContext); - - var traversalContext = it.registrar.apply(new TraversalContext(), property); - - var propertyPath = context.getPersistentPropertyPath(it.name, - WithContext.class); - - assertThat(accessor.getProperty(propertyPath, traversalContext)).isEqualTo("value"); - }); - } - - @Test // DATACMNS-1555 - public void traversalContextRejectsInvalidPropertyHandler() { - - PersistentEntity entity = context.getPersistentEntity(WithContext.class); - PersistentProperty property = entity.getRequiredPersistentProperty("collection"); - - var traversal = new TraversalContext(); - - assertThatIllegalArgumentException() // - .isThrownBy(() -> traversal.registerHandler(property, Map.class, Function.identity())); - } - @Value static class Order { Customer customer; @@ -208,23 +88,4 @@ public class PersistentPropertyAccessorUnitTests { static class Outer { NestedImmutable immutable; } - - // DATACMNS-1555 - - @Builder - static class WithContext { - - Collection collection; - List list; - Set set; - Map map; - String string; - } - - @Value(staticConstructor = "of") - static class Spec { - - String name; - BiFunction, TraversalContext> registrar; - } }