Skip to content

Commit 0035e43

Browse files
committed
Skip shadowed properties that are not assignable to their shadowing type.
1 parent acdc58c commit 0035e43

File tree

2 files changed

+109
-28
lines changed

2 files changed

+109
-28
lines changed

src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java

+68-21
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.beans.PropertyDescriptor;
1919
import java.lang.reflect.Field;
20+
import java.lang.reflect.Method;
2021
import java.lang.reflect.Modifier;
2122
import java.util.Collection;
2223
import java.util.Collections;
@@ -32,6 +33,7 @@
3233

3334
import org.slf4j.Logger;
3435
import org.slf4j.LoggerFactory;
36+
3537
import org.springframework.beans.BeanUtils;
3638
import org.springframework.beans.BeansException;
3739
import org.springframework.beans.factory.InitializingBean;
@@ -64,7 +66,6 @@
6466
import org.springframework.data.util.TypeInformation;
6567
import org.springframework.lang.Nullable;
6668
import org.springframework.util.Assert;
67-
import org.springframework.util.ClassUtils;
6869
import org.springframework.util.ReflectionUtils;
6970
import org.springframework.util.ReflectionUtils.FieldCallback;
7071
import org.springframework.util.ReflectionUtils.FieldFilter;
@@ -555,10 +556,9 @@ private void createAndRegisterProperty(Property input) {
555556
return;
556557
}
557558

558-
if (isKotlinOverride(property, input)) {
559+
if (shouldSkipOverrideProperty(property)) {
559560
return;
560561
}
561-
562562
entity.addPersistentProperty(property);
563563

564564
if (property.isAssociation()) {
@@ -572,39 +572,86 @@ private void createAndRegisterProperty(Property input) {
572572
property.getPersistentEntityTypes().forEach(AbstractMappingContext.this::addPersistentEntity);
573573
}
574574

575-
private boolean isKotlinOverride(P property, Property input) {
575+
protected boolean shouldSkipOverrideProperty(P property) {
576576

577-
if (!KotlinDetector.isKotlinPresent() || !input.getField().isPresent()) {
578-
return false;
579-
}
577+
P existingProperty = entity.getPersistentProperty(property.getName());
580578

581-
Field field = input.getField().get();
582-
if (!KotlinDetector.isKotlinType(field.getDeclaringClass())) {
579+
if (existingProperty == null) {
583580
return false;
584581
}
585582

586-
for (P existingProperty : entity) {
583+
Class<?> declaringClass = getDeclaringClass(property);
584+
Class<?> existingDeclaringClass = getDeclaringClass(existingProperty);
587585

588-
if (!property.getName().equals(existingProperty.getName())) {
589-
continue;
590-
}
586+
Class<?> propertyType = getPropertyType(property);
587+
Class<?> existingPropertyType = getPropertyType(existingProperty);
591588

592-
if (field.getDeclaringClass() != entity.getType()
593-
&& ClassUtils.isAssignable(field.getDeclaringClass(), entity.getType())) {
589+
if (!existingPropertyType.isAssignableFrom(propertyType)) {
594590

595-
if (LOGGER.isTraceEnabled()) {
596-
LOGGER.trace(String.format("Skipping '%s.%s' property declaration shadowed by '%s %s' in '%s'. ",
597-
field.getDeclaringClass().getName(), property.getName(), property.getType().getSimpleName(),
598-
property.getName(), entity.getType().getSimpleName()));
599-
}
600-
return true;
591+
if (LOGGER.isDebugEnabled()) {
592+
LOGGER.warn(String.format("Offending property declaration in '%s %s.%s' shadowing '%s %s.%s' in '%s'. ",
593+
propertyType.getSimpleName(), declaringClass.getName(), property.getName(),
594+
existingPropertyType.getSimpleName(), existingDeclaringClass.getName(), existingProperty.getName(),
595+
entity.getType().getSimpleName()));
601596
}
597+
598+
return true;
602599
}
603600

604601
return false;
605602
}
603+
604+
private Class<?> getDeclaringClass(PersistentProperty<?> persistentProperty) {
605+
606+
Field field = persistentProperty.getField();
607+
if (field != null) {
608+
return field.getDeclaringClass();
609+
}
610+
611+
Method accessor = persistentProperty.getGetter();
612+
613+
if (accessor == null) {
614+
accessor = persistentProperty.getSetter();
615+
}
616+
617+
if (accessor == null) {
618+
accessor = persistentProperty.getWither();
619+
}
620+
621+
if (accessor != null) {
622+
return accessor.getDeclaringClass();
623+
}
624+
625+
return persistentProperty.getOwner().getType();
626+
}
627+
628+
private Class<?> getPropertyType(PersistentProperty<?> persistentProperty) {
629+
630+
Field field = persistentProperty.getField();
631+
if (field != null) {
632+
return field.getType();
633+
}
634+
635+
Method getter = persistentProperty.getGetter();
636+
if (getter != null) {
637+
return getter.getReturnType();
638+
}
639+
640+
Method setter = persistentProperty.getSetter();
641+
if (setter != null) {
642+
return setter.getParameterTypes()[0];
643+
}
644+
645+
Method wither = persistentProperty.getWither();
646+
if (wither != null) {
647+
return wither.getParameterTypes()[0];
648+
}
649+
650+
return persistentProperty.getType();
651+
}
606652
}
607653

654+
608655
/**
609656
* Filter rejecting static fields as well as artificially introduced ones. See
610657
* {@link PersistentPropertyFilter#UNMAPPED_PROPERTIES} for details.

src/test/java/org/springframework/data/mapping/context/AbstractMappingContextUnitTests.java

+41-7
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
*/
6363
class AbstractMappingContextUnitTests {
6464

65-
SampleMappingContext context;
65+
private SampleMappingContext context;
6666

6767
@BeforeEach
6868
void setUp() {
@@ -224,27 +224,28 @@ void cleansUpCacheForRuntimeException() {
224224
}
225225

226226
@Test // DATACMNS-1509
227-
public void shouldIgnoreKotlinOverrideCtorPropertyInSuperClass() {
227+
void shouldIgnoreKotlinOverrideCtorPropertyInSuperClass() {
228228

229229
BasicPersistentEntity<Object, SamplePersistentProperty> entity = context
230230
.getPersistentEntity(ClassTypeInformation.from(ShadowingPropertyTypeWithCtor.class));
231231
entity.doWithProperties((PropertyHandler<SamplePersistentProperty>) property -> {
232-
assertThat(property.getField().getDeclaringClass()).isNotEqualTo(ShadowedPropertyTypeWithCtor.class);
232+
assertThat(property.getField().getDeclaringClass()).isIn(ShadowingPropertyTypeWithCtor.class,
233+
ShadowedPropertyTypeWithCtor.class);
233234
});
234235
}
235236

236237
@Test // DATACMNS-1509
237-
public void shouldIgnoreKotlinOverridePropertyInSuperClass() {
238+
void shouldIncludeAssignableKotlinOverridePropertyInSuperClass() {
238239

239240
BasicPersistentEntity<Object, SamplePersistentProperty> entity = context
240241
.getPersistentEntity(ClassTypeInformation.from(ShadowingPropertyType.class));
241242
entity.doWithProperties((PropertyHandler<SamplePersistentProperty>) property -> {
242-
assertThat(property.getField().getDeclaringClass()).isNotEqualTo(ShadowedPropertyType.class);
243+
assertThat(property.getField().getDeclaringClass()).isIn(ShadowedPropertyType.class, ShadowingPropertyType.class);
243244
});
244245
}
245246

246247
@Test // DATACMNS-1509
247-
public void shouldStillIncludeNonKotlinShadowedPropertyInSuperClass() {
248+
void shouldIncludeAssignableShadowedPropertyInSuperClass() {
248249

249250
BasicPersistentEntity<Object, SamplePersistentProperty> entity = context
250251
.getPersistentEntity(ClassTypeInformation.from(ShadowingProperty.class));
@@ -254,6 +255,16 @@ public void shouldStillIncludeNonKotlinShadowedPropertyInSuperClass() {
254255
).isNotEmpty();
255256
}
256257

258+
@Test // DATACMNS-1509
259+
void shouldIgnoreOverridePropertyInSuperClass() {
260+
261+
BasicPersistentEntity<Object, SamplePersistentProperty> entity = context
262+
.getPersistentEntity(ClassTypeInformation.from(ShadowingPropertyNotAssignable.class));
263+
entity.doWithProperties((PropertyHandler<SamplePersistentProperty>) property -> {
264+
assertThat(property.getField().getDeclaringClass()).isEqualTo(ShadowingPropertyNotAssignable.class);
265+
});
266+
}
267+
257268
private static void assertHasEntityFor(Class<?> type, SampleMappingContext context, boolean expected) {
258269

259270
boolean found = false;
@@ -275,7 +286,7 @@ class Person {
275286
LocalDateTime date;
276287
}
277288

278-
class Unsupported {
289+
private class Unsupported {
279290

280291
}
281292

@@ -377,4 +388,27 @@ public String getValue() {
377388
}
378389
}
379390

391+
static class ShadowedPropertyNotAssignable {
392+
393+
private String value;
394+
395+
}
396+
397+
static class ShadowingPropertyNotAssignable extends ShadowedPropertyNotAssignable {
398+
399+
private int value;
400+
401+
ShadowingPropertyNotAssignable(int value) {
402+
this.value = value;
403+
}
404+
405+
public void setValue(int value) {
406+
this.value = value;
407+
}
408+
409+
public int getValue() {
410+
return value;
411+
}
412+
}
413+
380414
}

0 commit comments

Comments
 (0)