/*
* Copyright 2011-2017 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
*
* http://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.model;
import static org.assertj.core.api.Assertions.*;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
import java.util.TreeSet;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.Person;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.ReflectionUtils;
/**
* Unit tests for {@link AbstractPersistentProperty}.
*
* @author Oliver Gierke
* @author Christoph Strobl
*/
public class AbstractPersistentPropertyUnitTests {
TypeInformation<TestClassComplex> typeInfo;
PersistentEntity<TestClassComplex, SamplePersistentProperty> entity;
SimpleTypeHolder typeHolder;
@Before
public void setUp() {
typeInfo = ClassTypeInformation.from(TestClassComplex.class);
entity = new BasicPersistentEntity<>(typeInfo);
typeHolder = new SimpleTypeHolder();
}
@Test // DATACMNS-68
public void discoversComponentTypeCorrectly() throws Exception {
assertThat(getProperty(TestClassComplex.class, "testClassSet").getComponentType()).hasValue(Object.class);
}
@Test // DATACMNS-101
public void returnsNestedEntityTypeCorrectly() {
assertThat(getProperty(TestClassComplex.class, "testClassSet").getPersistentEntityType()).isEmpty();
}
@Test // DATACMNS-132
public void isEntityWorksForUntypedMaps() throws Exception {
assertThat(getProperty(TestClassComplex.class, "map").isEntity()).isFalse();
}
@Test // DATACMNS-132
public void isEntityWorksForUntypedCollection() throws Exception {
assertThat(getProperty(TestClassComplex.class, "collection").isEntity()).isFalse();
}
@Test // DATACMNS-121
public void considersPropertiesEqualIfFieldEquals() {
SamplePersistentProperty firstProperty = getProperty(FirstConcrete.class, "genericField");
SamplePersistentProperty secondProperty = getProperty(SecondConcrete.class, "genericField");
assertThat(firstProperty).isEqualTo(secondProperty);
assertThat(firstProperty.hashCode()).isEqualTo(secondProperty.hashCode());
}
@Test // DATACMNS-180
public void doesNotConsiderJavaTransientFieldsTransient() {
assertThat(getProperty(TestClassComplex.class, "transientField").isTransient()).isFalse();
}
@Test // DATACMNS-206
public void findsSimpleGettersAndASetters() {
SamplePersistentProperty property = getProperty(AccessorTestClass.class, "id");
assertThat(property.getGetter()).isPresent();
assertThat(property.getSetter()).isPresent();
}
@Test // DATACMNS-206
public void doesNotUseInvalidGettersAndASetters() {
SamplePersistentProperty property = getProperty(AccessorTestClass.class, "anotherId");
assertThat(property.getGetter()).isNotPresent();
assertThat(property.getSetter()).isNotPresent();
}
@Test // DATACMNS-206
public void usesCustomGetter() {
SamplePersistentProperty property = getProperty(AccessorTestClass.class, "yetAnotherId");
assertThat(property.getGetter()).isPresent();
assertThat(property.getSetter()).isNotPresent();
}
@Test // DATACMNS-206
public void usesCustomSetter() {
SamplePersistentProperty property = getProperty(AccessorTestClass.class, "yetYetAnotherId");
assertThat(property.getGetter()).isNotPresent();
assertThat(property.getSetter()).isPresent();
}
@Test // DATACMNS-206
public void doesNotDiscoverGetterAndSetterIfNoPropertyDescriptorGiven() {
Field field = ReflectionUtils.findField(AccessorTestClass.class, "id");
PersistentProperty<SamplePersistentProperty> property = new SamplePersistentProperty(Property.of(field),
getEntity(AccessorTestClass.class), typeHolder);
assertThat(property.getGetter()).isNotPresent();
assertThat(property.getSetter()).isNotPresent();
}
@Test // DATACMNS-337
public void resolvesActualType() {
SamplePersistentProperty property = getProperty(Sample.class, "person");
assertThat(property.getActualType()).isEqualTo(Person.class);
property = getProperty(Sample.class, "persons");
assertThat(property.getActualType()).isEqualTo(Person.class);
property = getProperty(Sample.class, "personArray");
assertThat(property.getActualType()).isEqualTo(Person.class);
property = getProperty(Sample.class, "personMap");
assertThat(property.getActualType()).isEqualTo(Person.class);
}
@Test // DATACMNS-462
public void considersCollectionPropertyEntitiesIfComponentTypeIsEntity() {
SamplePersistentProperty property = getProperty(Sample.class, "persons");
assertThat(property.isEntity()).isTrue();
}
@Test // DATACMNS-462
public void considersMapPropertyEntitiesIfValueTypeIsEntity() {
SamplePersistentProperty property = getProperty(Sample.class, "personMap");
assertThat(property.isEntity()).isTrue();
}
@Test // DATACMNS-462
public void considersArrayPropertyEntitiesIfComponentTypeIsEntity() {
SamplePersistentProperty property = getProperty(Sample.class, "personArray");
assertThat(property.isEntity()).isTrue();
}
@Test // DATACMNS-462
public void considersCollectionPropertySimpleIfComponentTypeIsSimple() {
SamplePersistentProperty property = getProperty(Sample.class, "strings");
assertThat(property.isEntity()).isFalse();
}
@Test // DATACMNS-562
public void doesNotConsiderPropertyWithTreeMapMapValueAnEntity() {
SamplePersistentProperty property = getProperty(TreeMapWrapper.class, "map");
assertThat(property.getPersistentEntityType()).isEmpty();
assertThat(property.isEntity()).isFalse();
}
@Test // DATACMNS-867
public void resolvesFieldNameWithUnderscoresCorrectly() {
SamplePersistentProperty property = getProperty(TestClassComplex.class, "var_name_with_underscores");
assertThat(property.getName()).isEqualTo("var_name_with_underscores");
}
private <T> BasicPersistentEntity<T, SamplePersistentProperty> getEntity(Class<T> type) {
return new BasicPersistentEntity<>(ClassTypeInformation.from(type));
}
private <T> SamplePersistentProperty getProperty(Class<T> type, String name) {
Optional<Field> field = Optional.ofNullable(ReflectionUtils.findField(type, name));
Optional<PropertyDescriptor> descriptor = getPropertyDescriptor(type, name);
Property property = field
.map(it -> descriptor//
.map(foo -> Property.of(it, foo))//
.orElseGet(() -> Property.of(it)))
.orElseGet(() -> getPropertyDescriptor(type, name)//
.map(it -> Property.of(it))//
.orElseThrow(
() -> new IllegalArgumentException(String.format("Couldn't find property %s on %s!", name, type))));
return new SamplePersistentProperty(property, getEntity(type), typeHolder);
}
private static Optional<PropertyDescriptor> getPropertyDescriptor(Class<?> type, String propertyName) {
try {
return Arrays.stream(Introspector.getBeanInfo(type).getPropertyDescriptors())//
.filter(it -> it.getName().equals(propertyName))//
.findFirst();
} catch (IntrospectionException o_O) {
throw new RuntimeException(o_O);
}
}
class Generic<T> {
T genericField;
}
class FirstConcrete extends Generic<String> {
}
class SecondConcrete extends Generic<Integer> {
}
@SuppressWarnings("serial")
class TestClassSet extends TreeSet<Object> {}
@SuppressWarnings("rawtypes")
class TestClassComplex {
String id;
TestClassSet testClassSet;
Map map;
Collection collection;
transient Object transientField;
String var_name_with_underscores;
}
class AccessorTestClass {
// Valid getters and setters
Long id;
// Invalid getters and setters
Long anotherId;
// Customized getter
Number yetAnotherId;
// Customized setter
Number yetYetAnotherId;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getAnotherId() {
return anotherId.toString();
}
public void setAnotherId(String anotherId) {
this.anotherId = Long.parseLong(anotherId);
}
public Long getYetAnotherId() {
return null;
}
public void setYetYetAnotherId(Object yetYetAnotherId) {
this.yetYetAnotherId = null;
}
}
class SamplePersistentProperty extends AbstractPersistentProperty<SamplePersistentProperty> {
public SamplePersistentProperty(Property property, PersistentEntity<?, SamplePersistentProperty> owner,
SimpleTypeHolder simpleTypeHolder) {
super(property, owner, simpleTypeHolder);
}
public boolean isIdProperty() {
return false;
}
public boolean isVersionProperty() {
return false;
}
@Override
public boolean isAssociation() {
return false;
}
@Override
protected Association<SamplePersistentProperty> createAssociation() {
return null;
}
@Override
public <A extends Annotation> Optional<A> findAnnotation(Class<A> annotationType) {
return Optional.empty();
}
@Override
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
return false;
}
@Override
public <A extends Annotation> Optional<A> findPropertyOrOwnerAnnotation(Class<A> annotationType) {
return Optional.empty();
}
}
static class Sample {
Person person;
Collection<Person> persons;
Person[] personArray;
Map<String, Person> personMap;
Collection<String> strings;
}
class TreeMapWrapper {
TreeMap<String, TreeMap<String, String>> map;
}
}