/*
* 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.util;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import static org.springframework.data.util.ClassTypeInformation.*;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import org.assertj.core.api.OptionalAssert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
/**
* Unit tests for {@link ParameterizedTypeInformation}.
*
* @author Oliver Gierke
* @author Mark Paluch
*/
@RunWith(MockitoJUnitRunner.class)
public class ParameterizedTypeUnitTests {
static final Map<TypeVariable<?>, Type> EMPTY_MAP = Collections.emptyMap();
@Mock ParameterizedType one;
@Before
public void setUp() {
when(one.getActualTypeArguments()).thenReturn(new Type[0]);
}
@Test
public void considersTypeInformationsWithDifferingParentsNotEqual() {
TypeDiscoverer<String> stringParent = new TypeDiscoverer<>(String.class, EMPTY_MAP);
TypeDiscoverer<Object> objectParent = new TypeDiscoverer<>(Object.class, EMPTY_MAP);
ParameterizedTypeInformation<Object> first = new ParameterizedTypeInformation<>(one, stringParent, EMPTY_MAP);
ParameterizedTypeInformation<Object> second = new ParameterizedTypeInformation<>(one, objectParent, EMPTY_MAP);
assertThat(first).isNotEqualTo(second);
}
@Test
public void considersTypeInformationsWithSameParentsNotEqual() {
TypeDiscoverer<String> stringParent = new TypeDiscoverer<>(String.class, EMPTY_MAP);
ParameterizedTypeInformation<Object> first = new ParameterizedTypeInformation<>(one, stringParent, EMPTY_MAP);
ParameterizedTypeInformation<Object> second = new ParameterizedTypeInformation<>(one, stringParent, EMPTY_MAP);
assertThat(first.equals(second)).isTrue();
}
@Test // DATACMNS-88
public void resolvesMapValueTypeCorrectly() {
TypeInformation<Foo> type = ClassTypeInformation.from(Foo.class);
Optional<TypeInformation<?>> propertyType = type.getProperty("param");
OptionalAssert<TypeInformation<?>> assertion = assertThat(propertyType);
assertion.flatMap(it -> it.getProperty("value"))
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class));
assertion.flatMap(TypeInformation::getMapValueType)
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class));
propertyType = type.getProperty("param2");
assertion.flatMap(it -> it.getProperty("value"))
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class));
assertion.flatMap(TypeInformation::getMapValueType)
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class));
}
@Test // DATACMNS-446
public void createsToStringRepresentation() {
assertThat(from(Foo.class).getProperty("param")).map(Object::toString)
.hasValue("org.springframework.data.util.ParameterizedTypeUnitTests$Localized<java.lang.String>");
}
@Test // DATACMNS-485
public void hashCodeShouldBeConsistentWithEqualsForResolvedTypes() {
Optional<TypeInformation<?>> first = from(First.class).getProperty("property");
Optional<TypeInformation<?>> second = from(Second.class).getProperty("property");
assertThat(first).isEqualTo(second);
assertThat(first).hasValueSatisfying(left -> assertThat(second)
.hasValueSatisfying(right -> assertThat(left.hashCode()).isEqualTo(right.hashCode())));
}
@Test // DATACMNS-485
public void getActualTypeShouldNotUnwrapParameterizedTypes() {
Optional<TypeInformation<?>> type = from(First.class).getProperty("property");
assertThat(type).map(TypeInformation::getActualType).isEqualTo(type);
}
@Test // DATACMNS-697
public void usesLocalGenericInformationOfFields() {
TypeInformation<NormalizedProfile> information = ClassTypeInformation.from(NormalizedProfile.class);
assertThat(information.getProperty("education2.data"))//
.flatMap(TypeInformation::getComponentType)//
.flatMap(it -> it.getProperty("value"))//
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(Education.class));
}
@Test // DATACMNS-899
public void returnsEmptyOptionalMapValueTypeForNonMapProperties() {
OptionalAssert<TypeInformation<?>> assertion = assertThat(
ClassTypeInformation.from(Bar.class).getProperty("param"));
assertion.hasValueSatisfying(it -> assertThat(it).isInstanceOf(ParameterizedTypeInformation.class));
assertion.flatMap(TypeInformation::getMapValueType).isEmpty();
}
@SuppressWarnings("serial")
class Localized<S> extends HashMap<Locale, S> {
S value;
}
@SuppressWarnings("serial")
class Localized2<S> extends HashMap<S, Locale> {
S value;
}
class Foo {
Localized<String> param;
Localized2<String> param2;
}
class Bar {
List<String> param;
}
class Parameterized<T> {
T property;
}
class First {
Parameterized<String> property;
}
class Second {
Parameterized<String> property;
}
// see DATACMNS-697
class NormalizedProfile {
ListField<Education> education2;
}
class ListField<L> {
List<Value<L>> data;
}
class Value<T> {
T value;
}
class Education {}
}