diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PreferredConstructor.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PreferredConstructor.java index 55dee69da..82515bea5 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PreferredConstructor.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PreferredConstructor.java @@ -36,7 +36,7 @@ import org.springframework.util.StringUtils; public class PreferredConstructor> { private final Constructor constructor; - private final List> parameters; + private final List> parameters; /** * Creates a new {@link PreferredConstructor} from the given {@link Constructor} and {@link Parameter}s. @@ -44,7 +44,7 @@ public class PreferredConstructor> { * @param constructor * @param parameters */ - public PreferredConstructor(Constructor constructor, Parameter... parameters) { + public PreferredConstructor(Constructor constructor, Parameter... parameters) { Assert.notNull(constructor); Assert.notNull(parameters); @@ -68,7 +68,7 @@ public class PreferredConstructor> { * * @return */ - public Iterable> getParameters() { + public Iterable> getParameters() { return parameters; } @@ -121,6 +121,25 @@ public class PreferredConstructor> { return false; } + /** + * Returns whether the given {@link Parameter} is one referring to an enclosing class. That is in case the class this + * {@link PreferredConstructor} belongs to is a member class actually. If that's the case the compiler creates a first + * constructor argument of the enclosing class type. + * + * @param parameter must not be {@literal null}. + * @return + */ + public boolean isEnclosingClassParameter(Parameter parameter) { + + Assert.notNull(parameter); + + if (parameters.isEmpty()) { + return false; + } + + return parameters.get(0).equals(parameter) && parameter.isEnclosingClassParameter(); + } + /** * Value object to represent constructor parameters. * @@ -220,5 +239,11 @@ public class PreferredConstructor> { P referencedProperty = entity == null ? null : entity.getPersistentProperty(name); return property == null ? false : property.equals(referencedProperty); } + + private boolean isEnclosingClassParameter() { + + Class owningType = entity.getType(); + return owningType.isMemberClass() && type.getType().equals(owningType.getEnclosingClass()); + } } } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentEntityParameterValueProvider.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentEntityParameterValueProvider.java index a42895e34..bc138064f 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentEntityParameterValueProvider.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentEntityParameterValueProvider.java @@ -17,6 +17,7 @@ package org.springframework.data.mapping.model; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.PreferredConstructor; import org.springframework.data.mapping.PreferredConstructor.Parameter; import org.springframework.util.Assert; @@ -33,6 +34,7 @@ public class PersistentEntityParameterValueProvider

entity; private final PropertyValueProvider

provider; + private final Object parent; private SpELExpressionEvaluator spELEvaluator; @@ -42,14 +44,17 @@ public class PersistentEntityParameterValueProvider

entity, PropertyValueProvider

provider) { + public PersistentEntityParameterValueProvider(PersistentEntity entity, PropertyValueProvider

provider, + Object parent) { Assert.notNull(entity); Assert.notNull(provider); this.entity = entity; this.provider = provider; + this.parent = parent; } /** @@ -66,12 +71,19 @@ public class PersistentEntityParameterValueProvider

T getParameterValue(Parameter parameter) { if (spELEvaluator != null && parameter.hasSpelExpression()) { return spELEvaluator.evaluate(parameter.getSpelExpression()); } + PreferredConstructor constructor = entity.getPersistenceConstructor(); + + if (constructor.isEnclosingClassParameter(parameter)) { + return (T) parent; + } + P property = entity.getPersistentProperty(parameter.getName()); return provider.getPropertyValue(property); diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PreferredConstructorDiscoverer.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PreferredConstructorDiscoverer.java index d8e258aef..4c242d03d 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PreferredConstructorDiscoverer.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PreferredConstructorDiscoverer.java @@ -107,7 +107,7 @@ public class PreferredConstructorDiscoverer> } String[] parameterNames = nameDiscoverer.getParameterNames(constructor); - Parameter[] parameters = new Parameter[parameterTypes.size()]; + Parameter[] parameters = new Parameter[parameterTypes.size()]; Annotation[][] parameterAnnotations = constructor.getParameterAnnotations(); for (int i = 0; i < parameterTypes.size(); i++) { diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/convert/ReflectionEntityInstantiatorUnitTest.java b/spring-data-commons-core/src/test/java/org/springframework/data/convert/ReflectionEntityInstantiatorUnitTest.java index 9e99ce612..d728cee19 100644 --- a/spring-data-commons-core/src/test/java/org/springframework/data/convert/ReflectionEntityInstantiatorUnitTest.java +++ b/spring-data-commons-core/src/test/java/org/springframework/data/convert/ReflectionEntityInstantiatorUnitTest.java @@ -15,6 +15,7 @@ */ package org.springframework.data.convert; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import static org.springframework.data.convert.ReflectionEntityInstantiator.*; @@ -23,12 +24,16 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.data.convert.ReflectionEntityInstantiatorUnitTest.Outer.Inner; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PreferredConstructor; import org.springframework.data.mapping.PreferredConstructor.Parameter; +import org.springframework.data.mapping.model.BasicPersistentEntity; import org.springframework.data.mapping.model.ParameterValueProvider; import org.springframework.data.mapping.model.PreferredConstructorDiscoverer; +import org.springframework.data.util.ClassTypeInformation; +import org.springframework.test.util.ReflectionTestUtils; /** * Unit tests for {@link ReflectionEntityInstantiator}. @@ -78,10 +83,36 @@ public class ReflectionEntityInstantiatorUnitTest

entity = new BasicPersistentEntity(ClassTypeInformation.from(Inner.class)); + PreferredConstructor constructor = entity.getPersistenceConstructor(); + Parameter parameter = constructor.getParameters().iterator().next(); + + Object outer = new Outer(); + + when(provider.getParameterValue(parameter)).thenReturn(outer); + Inner instance = INSTANCE.createInstance(entity, provider); + + assertThat(instance, is(notNullValue())); + assertThat(ReflectionTestUtils.getField(instance, "this$1"), is(outer)); + } + static class Foo { Foo(String foo) { } } + + static class Outer { + + class Inner { + + } + } } diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PreferredConstructorDiscovererUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PreferredConstructorDiscovererUnitTests.java index 11b4626a6..4ad4a8559 100644 --- a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PreferredConstructorDiscovererUnitTests.java +++ b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/PreferredConstructorDiscovererUnitTests.java @@ -23,7 +23,10 @@ import java.util.Iterator; import org.junit.Test; import org.springframework.data.annotation.PersistenceConstructor; import org.springframework.data.mapping.PreferredConstructor.Parameter; +import org.springframework.data.mapping.PreferredConstructorDiscovererUnitTests.Outer.Inner; +import org.springframework.data.mapping.model.BasicPersistentEntity; import org.springframework.data.mapping.model.PreferredConstructorDiscoverer; +import org.springframework.data.util.ClassTypeInformation; /** * Unit tests for {@link PreferredConstructorDiscoverer}. @@ -76,13 +79,27 @@ public class PreferredConstructorDiscovererUnitTests

> parameters = constructor.getParameters().iterator(); + Iterator> parameters = constructor.getParameters().iterator(); Parameter parameter = parameters.next(); assertThat(parameter.getType().getType(), typeCompatibleWith(Long.class)); assertThat(parameters.hasNext(), is(false)); } + /** + * @see DATACMNS-134 + */ + @Test + public void discoversInnerClassConstructorCorrectly() { + + PersistentEntity entity = new BasicPersistentEntity(ClassTypeInformation.from(Inner.class)); + PreferredConstructorDiscoverer discoverer = new PreferredConstructorDiscoverer(entity); + PreferredConstructor constructor = discoverer.getConstructor(); + + Parameter parameter = constructor.getParameters().iterator().next(); + assertThat(constructor.isEnclosingClassParameter(parameter), is(true)); + } + static class EntityWithoutConstructor { } @@ -123,4 +140,11 @@ public class PreferredConstructorDiscovererUnitTests

> { + + @Mock + PropertyValueProvider

propertyValueProvider; + + /** + * @see DATACMNS-134 + */ + @Test + public void usesParentObjectAsImplicitFirstConstructorArgument() { + + Object outer = new Outer(); + + PersistentEntity entity = new BasicPersistentEntity(ClassTypeInformation.from(Inner.class)); + PreferredConstructor constructor = entity.getPersistenceConstructor(); + Iterator> iterator = constructor.getParameters().iterator(); + + ParameterValueProvider

provider = new PersistentEntityParameterValueProvider

(entity, propertyValueProvider, + outer); + assertThat(provider.getParameterValue(iterator.next()), is(outer)); + assertThat(provider.getParameterValue(iterator.next()), is(nullValue())); + assertThat(iterator.hasNext(), is(false)); + } + + static class Outer { + + class Inner { + + Inner(Object myObject) { + + } + } + } +}