switch to commons branch

This commit is contained in:
Christoph Strobl
2020-10-15 13:59:18 +02:00
parent e4f2085861
commit 569f9838d2
28 changed files with 103 additions and 4621 deletions

View File

@@ -26,7 +26,7 @@
<properties>
<project.type>multi</project.type>
<dist.id>spring-data-mongodb</dist.id>
<springdata.commons>2.4.0-SNAPSHOT</springdata.commons>
<springdata.commons>2.4.0-BUILD-TIME-DOMAIN-TYPE-METADATA-SNAPSHOT</springdata.commons>
<mongo>4.1.0</mongo>
<mongo.reactivestreams>${mongo}</mongo.reactivestreams>
<jmh.version>1.19</jmh.version>

View File

@@ -1,339 +0,0 @@
/*
* Copyright 2011-2020 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.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* Value object to encapsulate the constructor to be used when mapping persistent data to objects.
*
* @author Oliver Gierke
* @author Jon Brisbin
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
* @author Myeonghyeon Lee
*/
public class PreferredConstructor<T, P extends PersistentProperty<P>> {
private final Constructor<T> constructor;
private final List<Parameter<Object, P>> parameters;
private final Map<PersistentProperty<?>, Boolean> isPropertyParameterCache = new ConcurrentHashMap<>();
public PreferredConstructor() {
this.constructor = null;
this.parameters = Collections.emptyList();
}
/**
* Creates a new {@link PreferredConstructor} from the given {@link Constructor} and {@link Parameter}s.
*
* @param constructor must not be {@literal null}.
* @param parameters must not be {@literal null}.
*/
@SafeVarargs
public PreferredConstructor(Constructor<T> constructor, Parameter<Object, P>... parameters) {
Assert.notNull(constructor, "Constructor must not be null!");
Assert.notNull(parameters, "Parameters must not be null!");
ReflectionUtils.makeAccessible(constructor);
this.constructor = constructor;
this.parameters = Arrays.asList(parameters);
}
/**
* Returns the underlying {@link Constructor}.
*
* @return
*/
public Constructor<T> getConstructor() {
return constructor;
}
/**
* Returns the {@link Parameter}s of the constructor.
*
* @return
*/
public List<Parameter<Object, P>> getParameters() {
return parameters;
}
/**
* Returns whether the constructor has {@link Parameter}s.
*
* @see #isNoArgConstructor()
* @return
*/
public boolean hasParameters() {
return !parameters.isEmpty();
}
/**
* Returns whether the constructor does not have any arguments.
*
* @see #hasParameters()
* @return
*/
public boolean isNoArgConstructor() {
return parameters.isEmpty();
}
/**
* Returns whether the constructor was explicitly selected (by {@link PersistenceConstructor}).
*
* @return
*/
public boolean isExplicitlyAnnotated() {
return constructor.isAnnotationPresent(PersistenceConstructor.class);
}
/**
* Returns whether the given {@link PersistentProperty} is referenced in a constructor argument of the
* {@link PersistentEntity} backing this {@link PreferredConstructor}.
*
* @param property must not be {@literal null}.
* @return {@literal true} if the {@link PersistentProperty} is used in the constructor.
*/
public boolean isConstructorParameter(PersistentProperty<?> property) {
Assert.notNull(property, "Property must not be null!");
Boolean cached = isPropertyParameterCache.get(property);
if (cached != null) {
return cached;
}
boolean result = false;
for (Parameter<?, P> parameter : parameters) {
if (parameter.maps(property)) {
isPropertyParameterCache.put(property, true);
result = true;
break;
}
}
return result;
}
/**
* 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 {@literal true} if the {@link PersistentProperty} maps to the enclosing class.
*/
public boolean isEnclosingClassParameter(Parameter<?, P> parameter) {
Assert.notNull(parameter, "Parameter must not be null!");
if (parameters.isEmpty() || !parameter.isEnclosingClassParameter()) {
return false;
}
return parameters.get(0).equals(parameter);
}
/**
* Value object to represent constructor parameters.
*
* @param <T> the type of the parameter
* @author Oliver Gierke
*/
public static class Parameter<T, P extends PersistentProperty<P>> {
private final @Nullable String name;
private final TypeInformation<T> type;
private final String key;
private final @Nullable PersistentEntity<T, P> entity;
private final Lazy<Boolean> enclosingClassCache;
private final Lazy<Boolean> hasSpelExpression;
/**
* Creates a new {@link Parameter} with the given name, {@link TypeInformation} as well as an array of
* {@link Annotation}s. Will inspect the annotations for an {@link Value} annotation to lookup a key or an SpEL
* expression to be evaluated.
*
* @param name the name of the parameter, can be {@literal null}
* @param type must not be {@literal null}
* @param annotations must not be {@literal null} but can be empty
* @param entity must not be {@literal null}.
*/
public Parameter(@Nullable String name, TypeInformation<T> type, Annotation[] annotations,
@Nullable PersistentEntity<T, P> entity) {
Assert.notNull(type, "Type must not be null!");
Assert.notNull(annotations, "Annotations must not be null!");
this.name = name;
this.type = type;
this.key = getValue(annotations);
this.entity = entity;
this.enclosingClassCache = Lazy.of(() -> {
if (entity == null) {
throw new IllegalStateException();
}
Class<T> owningType = entity.getType();
return owningType.isMemberClass() && type.getType().equals(owningType.getEnclosingClass());
});
this.hasSpelExpression = Lazy.of(() -> StringUtils.hasText(getSpelExpression()));
}
@Nullable
private static String getValue(Annotation[] annotations) {
return Arrays.stream(annotations)//
.filter(it -> it.annotationType() == Value.class)//
.findFirst().map(it -> ((Value) it).value())//
.filter(StringUtils::hasText).orElse(null);
}
/**
* Returns the name of the parameter.
*
* @return
*/
@Nullable
public String getName() {
return name;
}
/**
* Returns the {@link TypeInformation} of the parameter.
*
* @return
*/
public TypeInformation<T> getType() {
return type;
}
/**
* Returns the raw resolved type of the parameter.
*
* @return
*/
public Class<T> getRawType() {
return type.getType();
}
/**
* Returns the key to be used when looking up a source data structure to populate the actual parameter value.
*
* @return
*/
public String getSpelExpression() {
return key;
}
/**
* Returns whether the constructor parameter is equipped with a SpEL expression.
*
* @return
*/
public boolean hasSpelExpression() {
return this.hasSpelExpression.get();
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Parameter)) {
return false;
}
Parameter<?, ?> parameter = (Parameter<?, ?>) o;
if (!ObjectUtils.nullSafeEquals(name, parameter.name)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(type, parameter.type)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(key, parameter.key)) {
return false;
}
return ObjectUtils.nullSafeEquals(entity, parameter.entity);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(name);
result = 31 * result + ObjectUtils.nullSafeHashCode(type);
result = 31 * result + ObjectUtils.nullSafeHashCode(key);
result = 31 * result + ObjectUtils.nullSafeHashCode(entity);
return result;
}
/**
* Returns whether the {@link Parameter} maps the given {@link PersistentProperty}.
*
* @param property
* @return
*/
boolean maps(PersistentProperty<?> property) {
PersistentEntity<T, P> entity = this.entity;
String name = this.name;
P referencedProperty = entity == null ? null : name == null ? null : entity.getPersistentProperty(name);
return property.equals(referencedProperty);
}
private boolean isEnclosingClassParameter() {
return enclosingClassCache.get();
}
}
}

View File

@@ -1,34 +0,0 @@
/*
* Copyright 2020. 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;
import org.springframework.lang.Nullable;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public interface PreferredConstructorProvider<T> {
@Nullable
<P extends PersistentProperty<P>> PreferredConstructor<T, P> getPreferredConstructor();
default <P extends PersistentProperty<P>> PreferredConstructor<T, P> getPreferredConstructorOrDefault(PreferredConstructor<T, P> fallback) {
PreferredConstructor<T, P> preferredConstructor = getPreferredConstructor();
return preferredConstructor != null ? preferredConstructor : fallback;
}
}

View File

@@ -1,689 +0,0 @@
/*
* Copyright 2011-2020 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.context;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.core.KotlinDetector;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.mapping.PersistentPropertyPaths;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.mapping.model.BeanWrapperPropertyAccessorFactory;
import org.springframework.data.mapping.model.ClassGeneratingPropertyAccessorFactory;
import org.springframework.data.mapping.model.EntityInstantiators;
import org.springframework.data.mapping.model.InstantiationAwarePropertyAccessorFactory;
import org.springframework.data.mapping.model.MutablePersistentEntity;
import org.springframework.data.mapping.model.PersistentPropertyAccessorFactory;
import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.mapping.model.StaticPropertyAccessorFactory;
import org.springframework.data.spel.EvaluationContextProvider;
import org.springframework.data.spel.ExtensionAwareEvaluationContextProvider;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.DomainTypeInformation;
import org.springframework.data.util.KotlinReflectionUtils;
import org.springframework.data.util.Optionals;
import org.springframework.data.util.Streamable;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;
import org.springframework.util.ReflectionUtils.FieldFilter;
/**
* Base class to build mapping metadata and thus create instances of {@link PersistentEntity} and
* {@link PersistentProperty}.
* <p>
* The implementation uses a {@link ReentrantReadWriteLock} to make sure {@link PersistentEntity} are completely
* populated before accessing them from outside.
*
* @param <E> the concrete {@link PersistentEntity} type the {@link MappingContext} implementation creates
* @param <P> the concrete {@link PersistentProperty} type the {@link MappingContext} implementation creates
* @author Jon Brisbin
* @author Oliver Gierke
* @author Michael Hunger
* @author Thomas Darimont
* @author Tomasz Wysocki
* @author Mark Paluch
* @author Mikael Klamra
* @author Christoph Strobl
*/
public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?, P>, P extends PersistentProperty<P>>
implements MappingContext<E, P>, ApplicationEventPublisherAware, ApplicationContextAware, InitializingBean {
private static final boolean IN_NATIVE_IMAGE = System.getProperty("org.graalvm.nativeimage.imagecode") != null;
private final Optional<E> NONE = Optional.empty();
private final Map<TypeInformation<?>, Optional<E>> persistentEntities = new HashMap<>();
private final PersistentPropertyAccessorFactory persistentPropertyAccessorFactory;
private final PersistentPropertyPathFactory<E, P> persistentPropertyPathFactory;
private @Nullable ApplicationEventPublisher applicationEventPublisher;
private EvaluationContextProvider evaluationContextProvider = EvaluationContextProvider.DEFAULT;
private Set<? extends Class<?>> initialEntitySet = new HashSet<>();
private boolean strict = false;
private SimpleTypeHolder simpleTypeHolder = SimpleTypeHolder.DEFAULT;
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock read = lock.readLock();
private final Lock write = lock.writeLock();
protected AbstractMappingContext() {
this.persistentPropertyPathFactory = new PersistentPropertyPathFactory<>(this);
EntityInstantiators instantiators = new EntityInstantiators();
PersistentPropertyAccessorFactory accessorFactory = IN_NATIVE_IMAGE ? BeanWrapperPropertyAccessorFactory.INSTANCE
: new ClassGeneratingPropertyAccessorFactory();
this.persistentPropertyAccessorFactory = new InstantiationAwarePropertyAccessorFactory(accessorFactory,
instantiators);
}
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher(org.springframework.context.ApplicationEventPublisher)
*/
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.evaluationContextProvider = new ExtensionAwareEvaluationContextProvider(applicationContext);
if (applicationEventPublisher == null) {
this.applicationEventPublisher = applicationContext;
}
}
/**
* Sets the {@link Set} of types to populate the context initially.
*
* @param initialEntitySet
*/
public void setInitialEntitySet(Set<? extends Class<?>> initialEntitySet) {
this.initialEntitySet = initialEntitySet;
}
/**
* Configures whether the {@link MappingContext} is in strict mode which means, that it will throw
* {@link MappingException}s in case one tries to lookup a {@link PersistentEntity} not already in the context. This
* defaults to {@literal false} so that unknown types will be transparently added to the MappingContext if not known
* in advance.
*
* @param strict
*/
public void setStrict(boolean strict) {
this.strict = strict;
}
/**
* Configures the {@link SimpleTypeHolder} to be used by the {@link MappingContext}. Allows customization of what
* types will be regarded as simple types and thus not recursively analyzed.
*
* @param simpleTypes must not be {@literal null}.
*/
public void setSimpleTypeHolder(SimpleTypeHolder simpleTypes) {
Assert.notNull(simpleTypes, "SimpleTypeHolder must not be null!");
this.simpleTypeHolder = simpleTypes;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.MappingContext#getPersistentEntities()
*/
@Override
public Collection<E> getPersistentEntities() {
try {
read.lock();
return persistentEntities.values().stream()//
.flatMap(Optionals::toStream)//
.collect(Collectors.toSet());
} finally {
read.unlock();
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.MappingContext#getPersistentEntity(java.lang.Class)
*/
@Nullable
public E getPersistentEntity(Class<?> type) {
return getPersistentEntity(ClassTypeInformation.from(type));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.MappingContext#hasPersistentEntityFor(java.lang.Class)
*/
@Override
public boolean hasPersistentEntityFor(Class<?> type) {
Assert.notNull(type, "Type must not be null!");
Optional<E> entity = persistentEntities.get(ClassTypeInformation.from(type));
return entity == null ? false : entity.isPresent();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.MappingContext#getPersistentEntity(org.springframework.data.util.TypeInformation)
*/
@Nullable
@Override
public E getPersistentEntity(TypeInformation<?> type) {
Assert.notNull(type, "Type must not be null!");
try {
read.lock();
Optional<E> entity = persistentEntities.get(type);
if (entity != null) {
return entity.orElse(null);
}
} finally {
read.unlock();
}
if (!shouldCreatePersistentEntityFor(type)) {
try {
write.lock();
persistentEntities.put(type, NONE);
} finally {
write.unlock();
}
return null;
}
if (strict) {
throw new MappingException("Unknown persistent entity " + type);
}
return addPersistentEntity(type).orElse(null);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.MappingContext#getPersistentEntity(org.springframework.data.mapping.PersistentProperty)
*/
@Nullable
@Override
public E getPersistentEntity(P persistentProperty) {
Assert.notNull(persistentProperty, "PersistentProperty must not be null!");
if (!persistentProperty.isEntity()) {
return null;
}
TypeInformation<?> typeInfo = persistentProperty.getTypeInformation();
return getPersistentEntity(typeInfo.getRequiredActualType());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.MappingContext#getPersistentPropertyPath(java.lang.Class, java.lang.String)
*/
@Override
public PersistentPropertyPath<P> getPersistentPropertyPath(PropertyPath propertyPath) {
return persistentPropertyPathFactory.from(propertyPath);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.MappingContext#getPersistentPropertyPath(java.lang.String, java.lang.Class)
*/
@Override
public PersistentPropertyPath<P> getPersistentPropertyPath(String propertyPath, Class<?> type) {
return persistentPropertyPathFactory.from(type, propertyPath);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.MappingContext#findPersistentPropertyPath(java.lang.Class, java.util.function.Predicate)
*/
@Override
public <T> PersistentPropertyPaths<T, P> findPersistentPropertyPaths(Class<T> type, Predicate<? super P> predicate) {
Assert.notNull(type, "Type must not be null!");
Assert.notNull(predicate, "Selection predicate must not be null!");
return doFindPersistentPropertyPaths(type, predicate, it -> !it.isAssociation());
}
/**
* Actually looks up the {@link PersistentPropertyPaths} for the given type, selection predicate and traversal guard.
* Primary purpose is to allow sub-types to alter the default traversal guard, e.g. used by
* {@link #findPersistentPropertyPaths(Class, Predicate)}.
*
* @param type will never be {@literal null}.
* @param predicate will never be {@literal null}.
* @param traversalGuard will never be {@literal null}.
* @return will never be {@literal null}.
* @see #findPersistentPropertyPaths(Class, Predicate)
*/
protected final <T> PersistentPropertyPaths<T, P> doFindPersistentPropertyPaths(Class<T> type,
Predicate<? super P> predicate, Predicate<P> traversalGuard) {
return persistentPropertyPathFactory.from(ClassTypeInformation.from(type), predicate, traversalGuard);
}
/**
* Adds the given type to the {@link MappingContext}.
*
* @param type must not be {@literal null}.
* @return
*/
protected Optional<E> addPersistentEntity(Class<?> type) {
return addPersistentEntity(ClassTypeInformation.from(type));
}
/**
* Adds the given {@link TypeInformation} to the {@link MappingContext}.
*
* @param typeInformation must not be {@literal null}.
* @return
*/
protected Optional<E> addPersistentEntity(TypeInformation<?> typeInformation) {
Assert.notNull(typeInformation, "TypeInformation must not be null!");
try {
read.lock();
Optional<E> persistentEntity = persistentEntities.get(typeInformation);
if (persistentEntity != null) {
return persistentEntity;
}
} finally {
read.unlock();
}
Class<?> type = typeInformation.getType();
E entity = null;
try {
write.lock();
entity = createPersistentEntity(typeInformation);
entity.setEvaluationContextProvider(evaluationContextProvider);
// Eagerly cache the entity as we might have to find it during recursive lookups.
persistentEntities.put(typeInformation, Optional.of(entity));
if (typeInformation instanceof DomainTypeInformation<?>) {
final E pEntity = entity;
((DomainTypeInformation<?>) typeInformation).doWithFields((fieldName, field) -> {
System.out.println("Creating PersistentProperty for " + fieldName + " via static configuration.");
P target = createPersistentProperty(
Property.of(field.getTypeInformation(), fieldName, field.getAnnotations()), pEntity, simpleTypeHolder);
pEntity.addPersistentProperty(target);
});
pEntity.setPersistentPropertyAccessorFactory(StaticPropertyAccessorFactory.instance());
return Optional.of(pEntity);
}
PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(type);
final Map<String, PropertyDescriptor> descriptors = new HashMap<>();
for (PropertyDescriptor descriptor : pds) {
descriptors.put(descriptor.getName(), descriptor);
}
try {
PersistentPropertyCreator persistentPropertyCreator = new PersistentPropertyCreator(entity, descriptors);
ReflectionUtils.doWithFields(type, persistentPropertyCreator, PersistentPropertyFilter.INSTANCE);
persistentPropertyCreator.addPropertiesForRemainingDescriptors();
entity.verify();
if (persistentPropertyAccessorFactory.isSupported(entity)) {
entity.setPersistentPropertyAccessorFactory(persistentPropertyAccessorFactory);
}
} catch (RuntimeException e) {
persistentEntities.remove(typeInformation);
throw e;
}
} catch (BeansException e) {
throw new MappingException(e.getMessage(), e);
} finally {
write.unlock();
}
// Inform listeners
if (applicationEventPublisher != null && entity != null) {
applicationEventPublisher.publishEvent(new MappingContextEvent<>(this, entity));
}
return Optional.of(entity);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentEntityAware#getManagedTypes()
*/
@Override
public Collection<TypeInformation<?>> getManagedTypes() {
try {
read.lock();
return Collections.unmodifiableSet(new HashSet<>(persistentEntities.keySet()));
} finally {
read.unlock();
}
}
/**
* Creates the concrete {@link PersistentEntity} instance.
*
* @param <T>
* @param typeInformation
* @return
*/
protected abstract <T> E createPersistentEntity(TypeInformation<T> typeInformation);
/**
* Creates the concrete instance of {@link PersistentProperty}.
*
* @param property
* @param owner
* @param simpleTypeHolder
* @return
*/
protected abstract P createPersistentProperty(Property property, E owner, SimpleTypeHolder simpleTypeHolder);
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() {
initialize();
}
/**
* Initializes the mapping context. Will add the types configured through {@link #setInitialEntitySet(Set)} to the
* context.
*/
public void initialize() {
initialEntitySet.forEach(this::addPersistentEntity);
}
/**
* Returns whether a {@link PersistentEntity} instance should be created for the given {@link TypeInformation}. By
* default this will reject all types considered simple and non-supported Kotlin classes, but it might be necessary to
* tweak that in case you have registered custom converters for top level types (which renders them to be considered
* simple) but still need meta-information about them.
* <p/>
*
* @param type will never be {@literal null}.
* @return
*/
protected boolean shouldCreatePersistentEntityFor(TypeInformation<?> type) {
if (simpleTypeHolder.isSimpleType(type.getType())) {
return false;
}
return !KotlinDetector.isKotlinType(type.getType()) || KotlinReflectionUtils.isSupportedKotlinClass(type.getType());
}
/**
* {@link FieldCallback} to create {@link PersistentProperty} instances.
*
* @author Oliver Gierke
*/
private final class PersistentPropertyCreator implements FieldCallback {
private final E entity;
private final Map<String, PropertyDescriptor> descriptors;
private final Map<String, PropertyDescriptor> remainingDescriptors;
public PersistentPropertyCreator(E entity, Map<String, PropertyDescriptor> descriptors) {
this(entity, descriptors, descriptors);
}
private PersistentPropertyCreator(E entity, Map<String, PropertyDescriptor> descriptors,
Map<String, PropertyDescriptor> remainingDescriptors) {
this.entity = entity;
this.descriptors = descriptors;
this.remainingDescriptors = remainingDescriptors;
}
/*
* (non-Javadoc)
* @see org.springframework.util.ReflectionUtils.FieldCallback#doWith(java.lang.reflect.Field)
*/
public void doWith(Field field) {
String fieldName = field.getName();
TypeInformation<?> type = entity.getTypeInformation();
ReflectionUtils.makeAccessible(field);
Property property = Optional.ofNullable(descriptors.get(fieldName))//
.map(it -> Property.of(type, field, it))//
.orElseGet(() -> Property.of(type, field));
createAndRegisterProperty(property);
this.remainingDescriptors.remove(fieldName);
}
/**
* Adds {@link PersistentProperty} instances for all suitable {@link PropertyDescriptor}s without a backing
* {@link Field}.
*
* @see PersistentPropertyFilter
*/
public void addPropertiesForRemainingDescriptors() {
remainingDescriptors.values().stream() //
.filter(Property::supportsStandalone) //
.map(it -> Property.of(entity.getTypeInformation(), it)) //
.filter(PersistentPropertyFilter.INSTANCE::matches) //
.forEach(this::createAndRegisterProperty);
}
private void createAndRegisterProperty(Property input) {
P property = createPersistentProperty(input, entity, simpleTypeHolder);
if (property.isTransient()) {
return;
}
if (!input.isFieldBacked() && !property.usePropertyAccess()) {
return;
}
entity.addPersistentProperty(property);
if (property.isAssociation()) {
entity.addAssociation(property.getRequiredAssociation());
}
if (entity.getType().equals(property.getRawType())) {
return;
}
property.getPersistentEntityTypes().forEach(AbstractMappingContext.this::addPersistentEntity);
}
}
/**
* Filter rejecting static fields as well as artificially introduced ones. See
* {@link PersistentPropertyFilter#UNMAPPED_PROPERTIES} for details.
*
* @author Oliver Gierke
*/
static enum PersistentPropertyFilter implements FieldFilter {
INSTANCE;
private static final Streamable<PropertyMatch> UNMAPPED_PROPERTIES;
static {
Set<PropertyMatch> matches = new HashSet<>();
matches.add(new PropertyMatch("class", null));
matches.add(new PropertyMatch("this\\$.*", null));
matches.add(new PropertyMatch("metaClass", "groovy.lang.MetaClass"));
UNMAPPED_PROPERTIES = Streamable.of(matches);
}
/*
* (non-Javadoc)
* @see org.springframework.util.ReflectionUtils.FieldFilter#matches(java.lang.reflect.Field)
*/
public boolean matches(Field field) {
if (Modifier.isStatic(field.getModifiers())) {
return false;
}
return !UNMAPPED_PROPERTIES.stream()//
.anyMatch(it -> it.matches(field.getName(), field.getType()));
}
/**
* Returns whether the given {@link PropertyDescriptor} is one to create a {@link PersistentProperty} for.
*
* @param property must not be {@literal null}.
* @return
*/
public boolean matches(Property property) {
Assert.notNull(property, "Property must not be null!");
if (!property.hasAccessor()) {
return false;
}
return !UNMAPPED_PROPERTIES.stream()//
.anyMatch(it -> it.matches(property.getName(), property.getType()));
}
/**
* Value object to help defining property exclusion based on name patterns and types.
*
* @since 1.4
* @author Oliver Gierke
*/
static class PropertyMatch {
private final @Nullable String namePattern;
private final @Nullable String typeName;
/**
* Creates a new {@link PropertyMatch} for the given name pattern and type name. At least one of the parameters
* must not be {@literal null}.
*
* @param namePattern a regex pattern to match field names, can be {@literal null}.
* @param typeName the name of the type to exclude, can be {@literal null}.
*/
public PropertyMatch(@Nullable String namePattern, @Nullable String typeName) {
Assert.isTrue(!(namePattern == null && typeName == null), "Either name pattern or type name must be given!");
this.namePattern = namePattern;
this.typeName = typeName;
}
/**
* Returns whether the given {@link Field} matches the defined {@link PropertyMatch}.
*
* @param name must not be {@literal null}.
* @param type must not be {@literal null}.
* @return
*/
public boolean matches(String name, Class<?> type) {
Assert.notNull(name, "Name must not be null!");
Assert.notNull(type, "Type must not be null!");
if (namePattern != null && !name.matches(namePattern)) {
return false;
}
if (typeName != null && !type.getName().equals(typeName)) {
return false;
}
return true;
}
}
}
}

View File

@@ -1,54 +0,0 @@
/*
* Copyright 2020. 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.
*/
/*
* Copyright 2020 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 java.util.function.BiFunction;
import java.util.function.Function;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public interface AccessorFunctionAware<S> {
default boolean hasSetFunctionFor(String fieldName) {
return getSetFunctionFor(fieldName) != null;
}
default boolean hasGetFunctionFor(String fieldName) {
return getGetFunctionFor(fieldName) != null;
}
BiFunction<S, Object, S> getSetFunctionFor(String fieldName);
Function<S, Object> getGetFunctionFor(String fieldName);
}

View File

@@ -1,331 +0,0 @@
/*
* Copyright 2011-2020 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.model;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.data.annotation.AccessType;
import org.springframework.data.annotation.AccessType.Type;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.ReadOnlyProperty;
import org.springframework.data.annotation.Reference;
import org.springframework.data.annotation.Transient;
import org.springframework.data.annotation.Version;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.Optionals;
import org.springframework.data.util.StreamUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Special {@link PersistentProperty} that takes annotations at a property into account.
*
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
*/
public abstract class AnnotationBasedPersistentProperty<P extends PersistentProperty<P>>
extends AbstractPersistentProperty<P> {
private static final String SPRING_DATA_PACKAGE = "org.springframework.data";
private final @Nullable String value;
private final Map<Class<? extends Annotation>, Optional<? extends Annotation>> annotationCache = new ConcurrentHashMap<>();
private final Lazy<Boolean> usePropertyAccess = Lazy.of(() -> {
AccessType accessType = findPropertyOrOwnerAnnotation(AccessType.class);
return accessType != null && Type.PROPERTY.equals(accessType.value()) || super.usePropertyAccess();
});
private final Lazy<Boolean> isTransient = Lazy.of(() -> super.isTransient() || isAnnotationPresent(Transient.class)
|| isAnnotationPresent(Value.class) || isAnnotationPresent(Autowired.class));
private final Lazy<Boolean> isWritable = Lazy
.of(() -> !isTransient() && !isAnnotationPresent(ReadOnlyProperty.class));
private final Lazy<Boolean> isReference = Lazy.of(() -> !isTransient() && isAnnotationPresent(Reference.class));
private final Lazy<Boolean> isId = Lazy.of(() -> isAnnotationPresent(Id.class));
private final Lazy<Boolean> isVersion = Lazy.of(() -> isAnnotationPresent(Version.class));
/**
* Creates a new {@link AnnotationBasedPersistentProperty}.
*
* @param property must not be {@literal null}.
* @param owner must not be {@literal null}.
*/
public AnnotationBasedPersistentProperty(Property property, PersistentEntity<?, P> owner,
SimpleTypeHolder simpleTypeHolder) {
super(property, owner, simpleTypeHolder);
populateAnnotationCache(property);
Value value = findAnnotation(Value.class);
this.value = value == null ? null : value.value();
}
/**
* Populates the annotation cache by eagerly accessing the annotations directly annotated to the accessors (if
* available) and the backing field. Annotations override annotations found on field.
*
* @param property
* @throws MappingException in case we find an ambiguous mapping on the accessor methods
*/
private void populateAnnotationCache(Property property) {
property.getAnnotations().forEach(it -> {
System.out.println("registering static annotation "+it.annotationType()+" for field " + property.getName());
annotationCache.put(it.annotationType(), Optional.of(it));
});
Optionals.toStream(property.getGetter(), property.getSetter()).forEach(it -> {
for (Annotation annotation : it.getAnnotations()) {
Class<? extends Annotation> annotationType = annotation.annotationType();
validateAnnotation(annotation,
"Ambiguous mapping! Annotation %s configured "
+ "multiple times on accessor methods of property %s in class %s!",
annotationType.getSimpleName(), getName(), getOwner().getType().getSimpleName());
annotationCache.put(annotationType,
Optional.ofNullable(AnnotatedElementUtils.findMergedAnnotation(it, annotationType)));
}
});
property.getField().ifPresent(it -> {
for (Annotation annotation : it.getAnnotations()) {
Class<? extends Annotation> annotationType = annotation.annotationType();
validateAnnotation(annotation,
"Ambiguous mapping! Annotation %s configured " + "on field %s and one of its accessor methods in class %s!",
annotationType.getSimpleName(), it.getName(), getOwner().getType().getSimpleName());
annotationCache.put(annotationType,
Optional.ofNullable(AnnotatedElementUtils.findMergedAnnotation(it, annotationType)));
}
});
}
/**
* Verifies the given annotation candidate detected. Will be rejected if it's a Spring Data annotation and we already
* found another one with a different configuration setup (i.e. other attribute values).
*
* @param candidate must not be {@literal null}.
* @param message must not be {@literal null}.
* @param arguments must not be {@literal null}.
*/
private void validateAnnotation(Annotation candidate, String message, Object... arguments) {
Class<? extends Annotation> annotationType = candidate.annotationType();
if (!annotationType.getName().startsWith(SPRING_DATA_PACKAGE)) {
return;
}
if (annotationCache.containsKey(annotationType)
&& !annotationCache.get(annotationType).equals(Optional.of(candidate))) {
throw new MappingException(String.format(message, arguments));
}
}
/**
* Inspects a potentially available {@link Value} annotation at the property and returns the {@link String} value of
* it.
*
* @see org.springframework.data.mapping.model.AbstractPersistentProperty#getSpelExpression()
*/
@Nullable
@Override
public String getSpelExpression() {
return value;
}
/**
* Considers plain transient fields, fields annotated with {@link Transient}, {@link Value} or {@link Autowired} as
* transient.
*
* @see org.springframework.data.mapping.PersistentProperty#isTransient()
*/
@Override
public boolean isTransient() {
return isTransient.get();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#isIdProperty()
*/
public boolean isIdProperty() {
return isId.get();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#isVersionProperty()
*/
public boolean isVersionProperty() {
return isVersion.get();
}
/**
* Considers the property an {@link Association} if it is annotated with {@link Reference}.
*/
@Override
public boolean isAssociation() {
return isReference.get();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.AbstractPersistentProperty#isWritable()
*/
@Override
public boolean isWritable() {
return isWritable.get();
}
/**
* Returns the annotation found for the current {@link AnnotationBasedPersistentProperty}. Will prefer getters or
* setters annotations over ones found at the backing field as the former can be used to reconfigure the metadata in
* subclasses.
*
* @param annotationType must not be {@literal null}.
* @return {@literal null} if annotation type not found on property.
*/
@Nullable
public <A extends Annotation> A findAnnotation(Class<A> annotationType) {
Assert.notNull(annotationType, "Annotation type must not be null!");
return doFindAnnotation(annotationType).orElse(null);
}
@SuppressWarnings("unchecked")
private <A extends Annotation> Optional<A> doFindAnnotation(Class<A> annotationType) {
Optional<? extends Annotation> annotation = annotationCache.get(annotationType);
if (annotation != null) {
return (Optional<A>) annotation;
}
return (Optional<A>) annotationCache.computeIfAbsent(annotationType, type -> {
return getAccessors() //
.map(it -> AnnotatedElementUtils.findMergedAnnotation(it, type)) //
.flatMap(StreamUtils::fromNullable) //
.findFirst();
});
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#findPropertyOrOwnerAnnotation(java.lang.Class)
*/
@Nullable
@Override
public <A extends Annotation> A findPropertyOrOwnerAnnotation(Class<A> annotationType) {
A annotation = findAnnotation(annotationType);
return annotation != null ? annotation : getOwner().findAnnotation(annotationType);
}
/**
* Returns whether the property carries the an annotation of the given type.
*
* @param annotationType the annotation type to look up.
* @return
*/
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
return doFindAnnotation(annotationType).isPresent();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.AbstractPersistentProperty#usePropertyAccess()
*/
@Override
public boolean usePropertyAccess() {
return usePropertyAccess.get();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getAssociationTargetType()
*/
@Nullable
@Override
public Class<?> getAssociationTargetType() {
Reference reference = findAnnotation(Reference.class);
if (reference == null) {
return isEntity() ? getActualType() : null;
}
Class<?> targetType = reference.to();
return Class.class.equals(targetType) //
? isEntity() ? getActualType() : null //
: targetType;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.AbstractPersistentProperty#toString()
*/
@Override
public String toString() {
if (annotationCache.isEmpty()) {
populateAnnotationCache(getProperty());
}
String builder = annotationCache.values().stream() //
.flatMap(Optionals::toStream) //
.map(Object::toString) //
.collect(Collectors.joining(" "));
return builder + super.toString();
}
private Stream<? extends AnnotatedElement> getAccessors() {
return Optionals.toStream(Optional.ofNullable(getGetter()), Optional.ofNullable(getSetter()),
Optional.ofNullable(getField()));
}
}

View File

@@ -1,656 +0,0 @@
/*
* Copyright 2011-2020 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.model;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.data.annotation.Immutable;
import org.springframework.data.annotation.TypeAlias;
import org.springframework.data.domain.Persistable;
import org.springframework.data.mapping.*;
import org.springframework.data.spel.EvaluationContextProvider;
import org.springframework.data.support.IsNewStrategy;
import org.springframework.data.support.PersistableIsNewStrategy;
import org.springframework.data.util.AnnotationAware;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.TypeInformation;
import org.springframework.expression.EvaluationContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.ConcurrentReferenceHashMap.ReferenceType;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
/**
* Simple value object to capture information of {@link PersistentEntity}s.
*
* @author Oliver Gierke
* @author Jon Brisbin
* @author Patryk Wasik
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
*/
public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implements MutablePersistentEntity<T, P> {
private static final String TYPE_MISMATCH = "Target bean of type %s is not of type of the persistent entity (%s)!";
private final @Nullable PreferredConstructor<T, P> constructor;
private final TypeInformation<T> information;
private final List<P> properties;
private final List<P> persistentPropertiesCache;
private final @Nullable Comparator<P> comparator;
private final Set<Association<P>> associations;
private final Map<String, P> propertyCache;
private final Map<Class<? extends Annotation>, Optional<Annotation>> annotationCache;
private final MultiValueMap<Class<? extends Annotation>, P> propertyAnnotationCache;
private @Nullable P idProperty;
private @Nullable P versionProperty;
private PersistentPropertyAccessorFactory propertyAccessorFactory;
private EvaluationContextProvider evaluationContextProvider = EvaluationContextProvider.DEFAULT;
private final Lazy<Alias> typeAlias;
private final Lazy<IsNewStrategy> isNewStrategy;
private final Lazy<Boolean> isImmutable;
private final Lazy<Boolean> requiresPropertyPopulation;
/**
* Creates a new {@link BasicPersistentEntity} from the given {@link TypeInformation}.
*
* @param information must not be {@literal null}.
*/
public BasicPersistentEntity(TypeInformation<T> information) {
this(information, null);
}
/**
* Creates a new {@link BasicPersistentEntity} for the given {@link TypeInformation} and {@link Comparator}. The given
* {@link Comparator} will be used to define the order of the {@link PersistentProperty} instances added to the
* entity.
*
* @param information must not be {@literal null}.
* @param comparator can be {@literal null}.
*/
public BasicPersistentEntity(TypeInformation<T> information, @Nullable Comparator<P> comparator) {
Assert.notNull(information, "Information must not be null!");
this.information = information;
this.properties = new ArrayList<>();
this.persistentPropertiesCache = new ArrayList<>();
this.comparator = comparator;
this.constructor = information instanceof PreferredConstructorProvider
? ((PreferredConstructorProvider<T>) information).getPreferredConstructor()
: PreferredConstructorDiscoverer.discover(this);
this.associations = comparator == null ? new HashSet<>() : new TreeSet<>(new AssociationComparator<>(comparator));
this.propertyCache = new HashMap<>(16, 1f);
this.annotationCache = new ConcurrentReferenceHashMap<>(16, ReferenceType.WEAK);
if(information instanceof AnnotationAware) {
for(Annotation annotation : ((AnnotationAware)information).getAnnotations()) {
annotationCache.put(annotation.annotationType(), Optional.of(annotation));
}
}
this.propertyAnnotationCache = CollectionUtils
.toMultiValueMap(new ConcurrentReferenceHashMap<>(16, ReferenceType.WEAK));
this.propertyAccessorFactory = information instanceof PersistentPropertyAccessorFactoryProvider
? ((PersistentPropertyAccessorFactoryProvider) information).getPersistentPropertyAccessorFactory()
: BeanWrapperPropertyAccessorFactory.INSTANCE;
this.typeAlias = Lazy.of(() -> getAliasFromAnnotation(getType()));
this.isNewStrategy = Lazy.of(() -> Persistable.class.isAssignableFrom(information.getType()) //
? PersistableIsNewStrategy.INSTANCE
: getFallbackIsNewStrategy());
this.isImmutable = Lazy.of(() -> isAnnotationPresent(Immutable.class));
this.requiresPropertyPopulation = Lazy.of(() -> !isImmutable() && properties.stream() //
.anyMatch(it -> !(isConstructorArgument(it) || it.isTransient())));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getPersistenceConstructor()
*/
@Nullable
public PreferredConstructor<T, P> getPersistenceConstructor() {
return constructor;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#isConstructorArgument(org.springframework.data.mapping.PersistentProperty)
*/
public boolean isConstructorArgument(PersistentProperty<?> property) {
return constructor != null && constructor.isConstructorParameter(property);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#isIdProperty(org.springframework.data.mapping.PersistentProperty)
*/
public boolean isIdProperty(PersistentProperty<?> property) {
return idProperty != null && idProperty.equals(property);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#isVersionProperty(org.springframework.data.mapping.PersistentProperty)
*/
public boolean isVersionProperty(PersistentProperty<?> property) {
return versionProperty != null && versionProperty.equals(property);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getName()
*/
public String getName() {
return getType().getName();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getIdProperty()
*/
@Nullable
public P getIdProperty() {
return idProperty;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getVersionProperty()
*/
@Nullable
public P getVersionProperty() {
return versionProperty;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#hasIdProperty()
*/
public boolean hasIdProperty() {
return idProperty != null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#hasVersionProperty()
*/
public boolean hasVersionProperty() {
return versionProperty != null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.MutablePersistentEntity#addPersistentProperty(P)
*/
public void addPersistentProperty(P property) {
Assert.notNull(property, "Property must not be null!");
if (properties.contains(property)) {
return;
}
properties.add(property);
if (!property.isTransient() && !property.isAssociation()) {
persistentPropertiesCache.add(property);
}
propertyCache.computeIfAbsent(property.getName(), key -> property);
P candidate = returnPropertyIfBetterIdPropertyCandidateOrNull(property);
if (candidate != null) {
this.idProperty = candidate;
}
if (property.isVersionProperty()) {
P versionProperty = this.versionProperty;
if (versionProperty != null) {
throw new MappingException(
String.format(
"Attempt to add version property %s but already have property %s registered "
+ "as version. Check your mapping configuration!",
property.getField(), versionProperty.getField()));
}
this.versionProperty = property;
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.MutablePersistentEntity#setEvaluationContextProvider(org.springframework.data.spel.EvaluationContextProvider)
*/
@Override
public void setEvaluationContextProvider(EvaluationContextProvider provider) {
this.evaluationContextProvider = provider;
}
/**
* Returns the given property if it is a better candidate for the id property than the current id property.
*
* @param property the new id property candidate, will never be {@literal null}.
* @return the given id property or {@literal null} if the given property is not an id property.
*/
@Nullable
protected P returnPropertyIfBetterIdPropertyCandidateOrNull(P property) {
if (!property.isIdProperty()) {
return null;
}
P idProperty = this.idProperty;
if (idProperty != null) {
throw new MappingException(String.format("Attempt to add id property %s but already have property %s registered "
+ "as id. Check your mapping configuration!", property.getField(), idProperty.getField()));
}
return property;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.MutablePersistentEntity#addAssociation(org.springframework.data.mapping.model.Association)
*/
public void addAssociation(Association<P> association) {
Assert.notNull(association, "Association must not be null!");
associations.add(association);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getPersistentProperty(java.lang.String)
*/
@Override
@Nullable
public P getPersistentProperty(String name) {
return propertyCache.get(name);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getPersistentProperties(java.lang.String)
*/
@Override
public Iterable<P> getPersistentProperties(Class<? extends Annotation> annotationType) {
Assert.notNull(annotationType, "Annotation type must not be null!");
return propertyAnnotationCache.computeIfAbsent(annotationType, this::doFindPersistentProperty);
}
private List<P> doFindPersistentProperty(Class<? extends Annotation> annotationType) {
List<P> annotatedProperties = properties.stream() //
.filter(it -> it.isAnnotationPresent(annotationType)) //
.collect(Collectors.toList());
if (!annotatedProperties.isEmpty()) {
return annotatedProperties;
}
return associations.stream() //
.map(Association::getInverse) //
.filter(it -> it.isAnnotationPresent(annotationType)).collect(Collectors.toList());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getType()
*/
public Class<T> getType() {
return information.getType();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getTypeAlias()
*/
public Alias getTypeAlias() {
return typeAlias.get();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getTypeInformation()
*/
public TypeInformation<T> getTypeInformation() {
return information;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#doWithProperties(org.springframework.data.mapping.PropertyHandler)
*/
public void doWithProperties(PropertyHandler<P> handler) {
Assert.notNull(handler, "PropertyHandler must not be null!");
for (P property : persistentPropertiesCache) {
handler.doWithPersistentProperty(property);
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#doWithProperties(org.springframework.data.mapping.PropertyHandler.Simple)
*/
@Override
public void doWithProperties(SimplePropertyHandler handler) {
Assert.notNull(handler, "Handler must not be null!");
for (PersistentProperty<?> property : persistentPropertiesCache) {
handler.doWithPersistentProperty(property);
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#doWithAssociations(org.springframework.data.mapping.AssociationHandler)
*/
public void doWithAssociations(AssociationHandler<P> handler) {
Assert.notNull(handler, "Handler must not be null!");
for (Association<P> association : associations) {
handler.doWithAssociation(association);
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#doWithAssociations(org.springframework.data.mapping.SimpleAssociationHandler)
*/
public void doWithAssociations(SimpleAssociationHandler handler) {
Assert.notNull(handler, "Handler must not be null!");
for (Association<? extends PersistentProperty<?>> association : associations) {
handler.doWithAssociation(association);
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#findAnnotation(java.lang.Class)
*/
@Nullable
@Override
public <A extends Annotation> A findAnnotation(Class<A> annotationType) {
return doFindAnnotation(annotationType).orElse(null);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#isAnnotationPresent(java.lang.Class)
*/
@Override
public <A extends Annotation> boolean isAnnotationPresent(Class<A> annotationType) {
return doFindAnnotation(annotationType).isPresent();
}
@SuppressWarnings("unchecked")
private <A extends Annotation> Optional<A> doFindAnnotation(Class<A> annotationType) {
return (Optional<A>) annotationCache.computeIfAbsent(annotationType,
it -> Optional.ofNullable(AnnotatedElementUtils.findMergedAnnotation(getType(), it)));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.MutablePersistentEntity#verify()
*/
public void verify() {
if (comparator != null) {
properties.sort(comparator);
persistentPropertiesCache.sort(comparator);
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.MutablePersistentEntity#setPersistentPropertyAccessorFactory(org.springframework.data.mapping.model.PersistentPropertyAccessorFactory)
*/
@Override
public void setPersistentPropertyAccessorFactory(PersistentPropertyAccessorFactory factory) {
this.propertyAccessorFactory = factory;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getPropertyAccessor(java.lang.Object)
*/
@Override
public <B> PersistentPropertyAccessor<B> getPropertyAccessor(B bean) {
verifyBeanType(bean);
return propertyAccessorFactory.getPropertyAccessor(this, bean);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getPropertyPathAccessor(java.lang.Object)
*/
@Override
public <B> PersistentPropertyPathAccessor<B> getPropertyPathAccessor(B bean) {
return new SimplePersistentPropertyPathAccessor<>(getPropertyAccessor(bean));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getIdentifierAccessor(java.lang.Object)
*/
@Override
public IdentifierAccessor getIdentifierAccessor(Object bean) {
verifyBeanType(bean);
if (Persistable.class.isAssignableFrom(getType())) {
return new PersistableIdentifierAccessor((Persistable<?>) bean);
}
return hasIdProperty() ? new IdPropertyIdentifierAccessor(this, bean) : new AbsentIdentifierAccessor(bean);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#isNew(java.lang.Object)
*/
@Override
public boolean isNew(Object bean) {
verifyBeanType(bean);
return isNewStrategy.get().isNew(bean);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#isImmutable()
*/
@Override
public boolean isImmutable() {
return isImmutable.get();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#requiresPropertyPopulation()
*/
@Override
public boolean requiresPropertyPopulation() {
return requiresPropertyPopulation.get();
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@Override
public Iterator<P> iterator() {
Iterator<P> iterator = properties.iterator();
return new Iterator<P>() {
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public P next() {
return iterator.next();
}
};
}
protected EvaluationContext getEvaluationContext(Object rootObject) {
return evaluationContextProvider.getEvaluationContext(rootObject);
}
/**
* Returns the default {@link IsNewStrategy} to be used. Will be a {@link PersistentEntityIsNewStrategy} by default.
* Note, that this strategy only gets used if the entity doesn't implement {@link Persistable} as this indicates the
* user wants to be in control over whether an entity is new or not.
*
* @return
* @since 2.1
*/
protected IsNewStrategy getFallbackIsNewStrategy() {
return PersistentEntityIsNewStrategy.of(this);
}
/**
* Verifies the given bean type to no be {@literal null} and of the type of the current {@link PersistentEntity}.
*
* @param bean must not be {@literal null}.
*/
private void verifyBeanType(Object bean) {
Assert.notNull(bean, "Target bean must not be null!");
Assert.isInstanceOf(getType(), bean,
() -> String.format(TYPE_MISMATCH, bean.getClass().getName(), getType().getName()));
}
/**
* Calculates the {@link Alias} to be used for the given type.
*
* @param type must not be {@literal null}.
* @return
*/
private static Alias getAliasFromAnnotation(Class<?> type) {
Optional<String> typeAliasValue = Optional
.ofNullable(AnnotatedElementUtils.findMergedAnnotation(type, TypeAlias.class))//
.map(TypeAlias::value)//
.filter(StringUtils::hasText);
return Alias.ofNullable(typeAliasValue.orElse(null));
}
/**
* A null-object implementation of {@link IdentifierAccessor} to be able to return an accessor for entities that do
* not have an identifier property.
*
* @author Oliver Gierke
*/
private static class AbsentIdentifierAccessor extends TargetAwareIdentifierAccessor {
public AbsentIdentifierAccessor(Object target) {
super(target);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.IdentifierAccessor#getIdentifier()
*/
@Override
@Nullable
public Object getIdentifier() {
return null;
}
}
/**
* Simple {@link Comparator} adaptor to delegate ordering to the inverse properties of the association.
*
* @author Oliver Gierke
*/
private static final class AssociationComparator<P extends PersistentProperty<P>>
implements Comparator<Association<P>>, Serializable {
private static final long serialVersionUID = 4508054194886854513L;
private final Comparator<P> delegate;
AssociationComparator(Comparator<P> delegate) {
this.delegate = delegate;
}
/*
* (non-Javadoc)
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(@Nullable Association<P> left, @Nullable Association<P> right) {
if (left == null) {
throw new IllegalArgumentException("Left argument must not be null!");
}
if (right == null) {
throw new IllegalArgumentException("Right argument must not be null!");
}
return delegate.compare(left.getInverse(), right.getInverse());
}
}
}

View File

@@ -1,103 +0,0 @@
/*
* Copyright 2012-2020 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.model;
import java.util.Collections;
import java.util.Map;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.util.Assert;
/**
* Simple value object allowing access to {@link EntityInstantiator} instances for a given type falling back to a
* default one.
*
* @author Oliver Drotbohm
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
* @since 2.3
*/
public class EntityInstantiators {
private final EntityInstantiator fallback;
private final Map<Class<?>, EntityInstantiator> customInstantiators;
/**
* Creates a new {@link EntityInstantiators} using the default fallback instantiator and no custom ones.
*/
public EntityInstantiators() {
this(Collections.emptyMap());
}
/**
* Creates a new {@link EntityInstantiators} using the given {@link EntityInstantiator} as fallback.
*
* @param fallback must not be {@literal null}.
*/
public EntityInstantiators(EntityInstantiator fallback) {
this(fallback, Collections.emptyMap());
}
/**
* Creates a new {@link EntityInstantiators} using the default fallback instantiator and the given custom ones.
*
* @param customInstantiators must not be {@literal null}.
*/
public EntityInstantiators(Map<Class<?>, EntityInstantiator> customInstantiators) {
this(new KotlinClassGeneratingEntityInstantiator(), customInstantiators);
}
/**
* Creates a new {@link EntityInstantiator} using the given fallback {@link EntityInstantiator} and the given custom
* ones.
*
* @param defaultInstantiator must not be {@literal null}.
* @param customInstantiators must not be {@literal null}.
*/
public EntityInstantiators(EntityInstantiator defaultInstantiator,
Map<Class<?>, EntityInstantiator> customInstantiators) {
Assert.notNull(defaultInstantiator, "DefaultInstantiator must not be null!");
Assert.notNull(customInstantiators, "CustomInstantiators must not be null!");
this.fallback = defaultInstantiator;
this.customInstantiators = customInstantiators;
}
/**
* Returns the {@link EntityInstantiator} to be used to create the given {@link PersistentEntity}.
*
* @param entity must not be {@literal null}.
* @return will never be {@literal null}.
*/
public EntityInstantiator getInstantiatorFor(PersistentEntity<?, ?> entity) {
Assert.notNull(entity, "Entity must not be null!");
Class<?> type = entity.getType();
if (!customInstantiators.containsKey(type)) {
if (entity.getTypeInformation() instanceof EntiyInstantiatorAware) {
return ((EntiyInstantiatorAware) entity.getTypeInformation()).getEntiyInstantiatorOrDefault(fallback);
}
return fallback;
}
EntityInstantiator instantiator = customInstantiators.get(entity.getType());
return instantiator == null ? fallback : instantiator;
}
}

View File

@@ -1,50 +0,0 @@
/*
* Copyright 2020. 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.
*/
/*
* Copyright 2020 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 org.springframework.lang.Nullable;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public interface EntiyInstantiatorAware {
@Nullable
EntityInstantiator getEntityInstantiator();
default EntityInstantiator getEntiyInstantiatorOrDefault(EntityInstantiator fallback) {
EntityInstantiator entityInstantiator = getEntityInstantiator();
return entityInstantiator != null ? entityInstantiator : fallback;
}
}

View File

@@ -1,35 +0,0 @@
/*
* Copyright 2020. 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 org.springframework.lang.Nullable;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public interface PersistentPropertyAccessorFactoryProvider {
@Nullable
PersistentPropertyAccessorFactory getPersistentPropertyAccessorFactory();
default PersistentPropertyAccessorFactory getPersistentPropertyAccessorFactoryOrDefault(
PersistentPropertyAccessorFactory fallback) {
PersistentPropertyAccessorFactory factory = getPersistentPropertyAccessorFactory();
return factory != null ? factory : fallback;
}
}

View File

@@ -1,352 +0,0 @@
/*
* Copyright 2016-2020 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.model;
import java.beans.FeatureDescriptor;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.Optionals;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* Value object to abstract the concept of a property backed by a {@link Field} and / or a {@link PropertyDescriptor}.
*
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
*/
public class Property {
private @Nullable TypeInformation<?> typeInformation;
private List<Annotation> annotations;
private final Optional<Field> field;
private final Optional<PropertyDescriptor> descriptor;
private final Class<?> rawType;
private final Lazy<Integer> hashCode;
private final Optional<Method> getter;
private final Optional<Method> setter;
private final Lazy<String> name;
private final Lazy<String> toString;
private final Lazy<Optional<Method>> wither;
private Property(String name, TypeInformation<?> typeInformation, List<Annotation> annotations) {
this.annotations = annotations;
this.typeInformation = typeInformation;
this.field = Optional.empty();
this.descriptor = Optional.empty();
this.rawType = typeInformation.getType();
this.hashCode = Lazy.of( () -> typeInformation.hashCode() + name.hashCode());
this.getter = Optional.empty();
this.setter = Optional.empty();
this.name = Lazy.of(name);
this.toString = Lazy.of(() -> typeInformation.toString() + name);
this.wither = Lazy.of(() -> findWither(typeInformation, getName(), getType()));
}
private Property(TypeInformation<?> type, Optional<Field> field, Optional<PropertyDescriptor> descriptor) {
Assert.notNull(type, "Type must not be null!");
Assert.isTrue(Optionals.isAnyPresent(field, descriptor), "Either field or descriptor has to be given!");
this.annotations = Collections.emptyList();
this.field = field;
this.descriptor = descriptor;
this.rawType = withFieldOrDescriptor( //
it -> type.getRequiredProperty(it.getName()).getType(), //
it -> type.getRequiredProperty(it.getName()).getType() //
);
this.hashCode = Lazy.of(() -> withFieldOrDescriptor(Object::hashCode));
this.name = Lazy.of(() -> withFieldOrDescriptor(Field::getName, FeatureDescriptor::getName));
this.toString = Lazy.of(() -> withFieldOrDescriptor(Object::toString,
it -> String.format("%s.%s", type.getType().getName(), it.getDisplayName())));
this.getter = descriptor.map(PropertyDescriptor::getReadMethod)//
.filter(it -> getType() != null)//
.filter(it -> getType().isAssignableFrom(type.getReturnType(it).getType()));
this.setter = descriptor.map(PropertyDescriptor::getWriteMethod)//
.filter(it -> getType() != null)//
.filter(it -> type.getParameterTypes(it).get(0).getType().isAssignableFrom(getType()));
this.wither = Lazy.of(() -> findWither(type, getName(), getType()));
}
/**
* Creates a new {@link Property} backed by the given field.
*
* @param type the owning type, must not be {@literal null}.
* @param field must not be {@literal null}.
* @return
*/
public static Property of(TypeInformation<?> type, Field field) {
Assert.notNull(field, "Field must not be null!");
return new Property(type, Optional.of(field), Optional.empty());
}
/**
* Creates a new {@link Property} backed by the given {@link Field} and {@link PropertyDescriptor}.
*
* @param type the owning type, must not be {@literal null}.
* @param field must not be {@literal null}.
* @param descriptor must not be {@literal null}.
* @return
*/
public static Property of(TypeInformation<?> type, Field field, PropertyDescriptor descriptor) {
Assert.notNull(field, "Field must not be null!");
Assert.notNull(descriptor, "PropertyDescriptor must not be null!");
return new Property(type, Optional.of(field), Optional.of(descriptor));
}
/**
* Creates a new {@link Property} backed by the given {@link Field} and {@link PropertyDescriptor}.
*
* @param type the owning type, must not be {@literal null}.
* @param field must not be {@literal null}.
* @param descriptor must not be {@literal null}.
* @return
*/
public static Property of(TypeInformation<?> type, String name) {
return new Property(name, type, Collections.emptyList());
}
public static Property of(TypeInformation<?> type, String name, List<Annotation> annotations) {
return new Property(name, type, annotations != null ? annotations : Collections.emptyList());
}
/**
* Creates a new {@link Property} for the given {@link PropertyDescriptor}. The creation might fail if the given
* property is not representing a proper property.
*
* @param type the owning type, must not be {@literal null}.
* @param descriptor must not be {@literal null}.
* @return
* @see #supportsStandalone(PropertyDescriptor)
*/
public static Property of(TypeInformation<?> type, PropertyDescriptor descriptor) {
Assert.notNull(descriptor, "PropertyDescriptor must not be null!");
return new Property(type, Optional.empty(), Optional.of(descriptor));
}
/**
* Returns whether the given {@link PropertyDescriptor} is supported in for standalone creation of a {@link Property}
* instance.
*
* @param descriptor
* @return
*/
public static boolean supportsStandalone(PropertyDescriptor descriptor) {
Assert.notNull(descriptor, "PropertyDescriptor must not be null!");
return descriptor.getPropertyType() != null;
}
/**
* Returns whether the property is backed by a field.
*
* @return
*/
public boolean isFieldBacked() {
return field.isPresent();
}
/**
* Returns the getter of the property if available and if it matches the type of the property.
*
* @return will never be {@literal null}.
*/
public Optional<Method> getGetter() {
return getter;
}
/**
* Returns the setter of the property if available and if its first (only) parameter matches the type of the property.
*
* @return will never be {@literal null}.
*/
public Optional<Method> getSetter() {
return setter;
}
/**
* Returns the wither of the property if available and if its first (only) parameter matches the type of the property.
*
* @return will never be {@literal null}.
*/
public Optional<Method> getWither() {
return wither.get();
}
/**
* Returns the field of the property if available and if its first (only) parameter matches the type of the property.
*
* @return will never be {@literal null}.
*/
public Optional<Field> getField() {
return this.field;
}
/**
* Returns whether the property exposes a getter or a setter.
*
* @return
*/
public boolean hasAccessor() {
return getGetter().isPresent() || getSetter().isPresent();
}
/**
* Returns the name of the property.
*
* @return will never be {@literal null}.
*/
public String getName() {
return this.name.get();
}
/**
* Returns the type of the property.
*
* @return will never be {@literal null}.
*/
public Class<?> getType() {
return rawType;
}
public List<Annotation> getAnnotations() {
return annotations;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Property)) {
return false;
}
Property that = (Property) obj;
if(this.typeInformation != null && that.typeInformation != null) {
if(this.typeInformation != that.typeInformation) {
return false;
}
if(!this.name.get().equals(that.name.get())) {
return false;
}
}
return this.field.isPresent() ? this.field.equals(that.field) : this.descriptor.equals(that.descriptor);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return hashCode.get();
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return toString.get();
}
/**
* Maps the backing {@link Field} or {@link PropertyDescriptor} using the given {@link Function}.
*
* @param function must not be {@literal null}.
* @return
*/
private <T> T withFieldOrDescriptor(Function<Object, T> function) {
return withFieldOrDescriptor(function, function);
}
/**
* Maps the backing {@link Field} or {@link PropertyDescriptor} using the given functions.
*
* @param field must not be {@literal null}.
* @param descriptor must not be {@literal null}.
* @return
*/
private <T> T withFieldOrDescriptor(Function<? super Field, T> field,
Function<? super PropertyDescriptor, T> descriptor) {
return Optionals.firstNonEmpty(//
() -> this.field.map(field), //
() -> this.descriptor.map(descriptor))//
.orElseThrow(() -> new IllegalStateException("Should not occur! Either field or descriptor has to be given"));
}
private static Optional<Method> findWither(TypeInformation<?> owner, String propertyName, Class<?> rawType) {
AtomicReference<Method> resultHolder = new AtomicReference<>();
String methodName = String.format("with%s", StringUtils.capitalize(propertyName));
ReflectionUtils.doWithMethods(owner.getType(), it -> {
if (owner.isAssignableFrom(owner.getReturnType(it))) {
resultHolder.set(it);
}
}, it -> isMethodWithSingleParameterOfType(it, methodName, rawType));
Method method = resultHolder.get();
return method != null ? Optional.of(method) : Optional.empty();
}
private static boolean isMethodWithSingleParameterOfType(Method method, String name, Class<?> type) {
return method.getParameterCount() == 1 //
&& method.getName().equals(name) //
&& method.getParameterTypes()[0].equals(type);
}
}

View File

@@ -1,114 +0,0 @@
/*
* Copyright 2020. 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.
*/
/*
* Copyright 2020 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 org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.lang.Nullable;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public class StaticPropertyAccessorFactory implements PersistentPropertyAccessorFactory {
private static final StaticPropertyAccessorFactory INSTANCE = new StaticPropertyAccessorFactory();
public static StaticPropertyAccessorFactory instance() {
return INSTANCE;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.PersistentPropertyAccessorFactory#getPropertyAccessor(org.springframework.data.mapping.PersistentEntity, java.lang.Object)
*/
@Override
public <T> PersistentPropertyAccessor<T> getPropertyAccessor(PersistentEntity<?, ?> entity, T bean) {
System.out.println("Obtaining static property acessor for entity " + entity.getName());
return new StaticPropertyAccessor<>((AccessorFunctionAware<T>) entity.getTypeInformation(), bean);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.PersistentPropertyAccessorFactory#isSupported(org.springframework.data.mapping.PersistentEntity)
*/
@Override
public boolean isSupported(PersistentEntity<?, ?> entity) {
boolean isStaticTypedEntity = entity.getTypeInformation() instanceof AccessorFunctionAware;
System.out.println(entity.getName() + " isStaticTypedEntity: " + isStaticTypedEntity);
return isStaticTypedEntity;
}
static class StaticPropertyAccessor<T> implements PersistentPropertyAccessor<T> {
T bean;
AccessorFunctionAware<T> accessorFunctionAware;
public StaticPropertyAccessor(AccessorFunctionAware<T> accessorFunctionAware, T bean) {
this.bean = bean;
this.accessorFunctionAware = accessorFunctionAware;
}
@Override
public void setProperty(PersistentProperty<?> property, @Nullable Object value) {
if (!accessorFunctionAware.hasSetFunctionFor(property.getName())) {
return;
}
this.bean = accessorFunctionAware.getSetFunctionFor(property.getName()).apply(bean, value);
System.out.println(
"setting value " + value + " via setter function for " + property.getName() + " resulting in " + bean);
}
@Nullable
@Override
public Object getProperty(PersistentProperty<?> property) {
if (!accessorFunctionAware.hasGetFunctionFor(property.getName())) {
return null;
}
Object value = accessorFunctionAware.getGetFunctionFor(property.getName()).apply(bean);
System.out.println("obtaining value " + value + " from getter function for " + property.getName());
return value;
}
@Override
public T getBean() {
return this.bean;
}
}
}

View File

@@ -1,91 +0,0 @@
/*
* Copyright 2020. 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.
*/
/*
* Copyright 2020 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 java.lang.annotation.Annotation;
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.EntityInstantiator;
import org.springframework.data.mapping.model.ParameterValueProvider;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public class AddressTypeInformation extends DomainTypeInformation<Address> {
private static final AddressTypeInformation INSTANCE = new AddressTypeInformation();
private AddressTypeInformation() {
super(Address.class);
}
public static AddressTypeInformation instance() {
return INSTANCE;
}
@Override
protected void computeFields() {
addField(Field.<Address> string("city").getter(Address::getCity));
addField(Field.<Address> string("street").getter(Address::getStreet));
}
@Override
protected EntityInstantiator computeEntityInstantiator() {
return new EntityInstantiator() {
@Override
public <T, E extends PersistentEntity<? extends T, P>, P extends PersistentProperty<P>> T createInstance(E entity,
ParameterValueProvider<P> provider) {
String city = (String) provider
.getParameterValue(new Parameter("city", new StringTypeInformation(), new Annotation[] {}, entity));
String street = (String) provider
.getParameterValue(new Parameter("street", new StringTypeInformation(), new Annotation[] {}, entity));
T address = (T) new Address(city, street);
System.out.println("Created new Address instance via constructor using values (" + city + ", " + street
+ ") resulting in " + address);
return address;
}
};
}
@Override
protected PreferredConstructor computePreferredConstructor() {
return StaticPreferredConstructor.of("city", "street");
}
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2020. 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 java.lang.annotation.Annotation;
import java.util.List;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public interface AnnotationAware {
List<Annotation> getAnnotations();
boolean hasAnnotation(Class<?> annotationType);
<T extends Annotation> List<T> findAnnotation(Class<T> annotation);
}

View File

@@ -1,192 +0,0 @@
/*
* Copyright 2011-2020 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.util;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.core.GenericTypeResolver;
import org.springframework.util.Assert;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.ConcurrentReferenceHashMap.ReferenceType;
/**
* {@link TypeInformation} for a plain {@link Class}.
*
* @author Oliver Gierke
* @author Christoph Strobl
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
public static final ClassTypeInformation<Collection> COLLECTION = new ClassTypeInformation(Collection.class);
public static final ClassTypeInformation<List> LIST = new ClassTypeInformation(List.class);
public static final ClassTypeInformation<Set> SET = new ClassTypeInformation(Set.class);
public static final ClassTypeInformation<Map> MAP = new ClassTypeInformation(Map.class);
public static final ClassTypeInformation<Object> OBJECT = new ClassTypeInformation(Object.class);
// cannot use reference hash map cause static type information might not be referenced from outside and get discarded
private static final Map<Class<?>, ClassTypeInformation<?>> cache = new ConcurrentHashMap<>();
static {
Arrays.asList(COLLECTION, LIST, SET, MAP, OBJECT).forEach(it -> cache.put(it.getType(), it));
}
private final Class<S> type;
public static void warmCache(ClassTypeInformation<?>... typeInformations) {
for(ClassTypeInformation<?> information : typeInformations) {
cache.put(information.getType(), information);
}
}
/**
* Simple factory method to easily create new instances of {@link ClassTypeInformation}.
*
* @param <S>
* @param type must not be {@literal null}.
* @return
*/
public static <S> ClassTypeInformation<S> from(Class<S> type) {
Assert.notNull(type, "Type must not be null!");
return (ClassTypeInformation<S>) cache.computeIfAbsent(type, ClassTypeInformation::new);
}
/**
* Creates a {@link TypeInformation} from the given method's return type.
*
* @param method must not be {@literal null}.
* @return
*/
public static <S> TypeInformation<S> fromReturnTypeOf(Method method) {
Assert.notNull(method, "Method must not be null!");
return (TypeInformation<S>) ClassTypeInformation.from(method.getDeclaringClass())
.createInfo(method.getGenericReturnType());
}
/**
* Creates {@link ClassTypeInformation} for the given type.
*
* @param type
*/
ClassTypeInformation(Class<S> type) {
super(ProxyUtils.getUserClass(type), getTypeVariableMap(type));
this.type = type;
}
ClassTypeInformation(Class<S> type, TypeInformation<?> componentType, TypeInformation<?> keyType) {
super(type, componentType, keyType);
this.type = type;
}
/**
* Little helper to allow us to create a generified map, actually just to satisfy the compiler.
*
* @param type must not be {@literal null}.
* @return
*/
private static Map<TypeVariable<?>, Type> getTypeVariableMap(Class<?> type) {
return getTypeVariableMap(type, new HashSet<>());
}
private static Map<TypeVariable<?>, Type> getTypeVariableMap(Class<?> type, Collection<Type> visited) {
if (visited.contains(type)) {
return Collections.emptyMap();
} else {
visited.add(type);
}
Map<TypeVariable, Type> source = GenericTypeResolver.getTypeVariableMap(type);
Map<TypeVariable<?>, Type> map = new HashMap<>(source.size());
for (Entry<TypeVariable, Type> entry : source.entrySet()) {
Type value = entry.getValue();
map.put(entry.getKey(), entry.getValue());
if (value instanceof Class) {
for (Entry<TypeVariable<?>, Type> nestedEntry : getTypeVariableMap((Class<?>) value, visited).entrySet()) {
if (!map.containsKey(nestedEntry.getKey())) {
map.put(nestedEntry.getKey(), nestedEntry.getValue());
}
}
}
}
return map;
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeDiscoverer#getType()
*/
@Override
public Class<S> getType() {
return type;
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeDiscoverer#getRawTypeInformation()
*/
@Override
public ClassTypeInformation<?> getRawTypeInformation() {
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeDiscoverer#isAssignableFrom(org.springframework.data.util.TypeInformation)
*/
@Override
public boolean isAssignableFrom(TypeInformation<?> target) {
return getType().isAssignableFrom(target.getType());
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeDiscoverer#specialize(org.springframework.data.util.ClassTypeInformation)
*/
@Override
public TypeInformation<? extends S> specialize(ClassTypeInformation<?> type) {
return (TypeInformation<? extends S>) type;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return type.getName();
}
}

View File

@@ -1,343 +0,0 @@
/*
* Copyright 2020. 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.
*/
/*
* Copyright 2020 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 java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.PreferredConstructorProvider;
import org.springframework.data.mapping.model.AccessorFunctionAware;
import org.springframework.data.mapping.model.EntityInstantiator;
import org.springframework.data.mapping.model.EntiyInstantiatorAware;
import org.springframework.data.mapping.model.PersistentPropertyAccessorFactory;
import org.springframework.data.mapping.model.PersistentPropertyAccessorFactoryProvider;
import org.springframework.data.mapping.model.StaticPropertyAccessorFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public class DomainTypeInformation<S> extends ClassTypeInformation<S>
implements AnnotationAware, EntiyInstantiatorAware, PreferredConstructorProvider<S>,
PersistentPropertyAccessorFactoryProvider, AccessorFunctionAware<S> {
private final Class<S> type;
@Nullable private final TypeInformation<?> componentType;
@Nullable private final TypeInformation<?> keyType;
private DomainTypeInformation<?> superTypeInformation;
private List<TypeInformation<?>> typeArguments;
private MultiValueMap<Class<? extends Annotation>, Annotation> annotations;
private final Fields fields;
private EntityInstantiator instantiator;
private PreferredConstructor<S, ?> preferredConstructor;
public DomainTypeInformation(Class<S> type) {
this(type, null, null);
}
public DomainTypeInformation(Class<S> type, @Nullable TypeInformation<?> componentType,
@Nullable TypeInformation<?> keyType) {
super(type, componentType, keyType);
this.type = type;
this.componentType = componentType;
this.keyType = keyType;
this.typeArguments = computeTypeArguments();
this.instantiator = computeEntityInstantiator();
this.preferredConstructor = computePreferredConstructor();
this.annotations = new LinkedMultiValueMap<>();
this.fields = new Fields(type);
computeFields();
computeAnnotations();
}
protected void addField(Field<?, S> field) {
this.fields.add(field);
}
protected List<TypeInformation<?>> computeTypeArguments() {
return Collections.emptyList();
}
protected EntityInstantiator computeEntityInstantiator() {
return null;
}
protected PreferredConstructor<S, ?> computePreferredConstructor() {
return null;
}
@Override
public PreferredConstructor<S, ?> getPreferredConstructor() {
return preferredConstructor;
}
protected void computeFields() {
//
}
protected void computeAnnotations() {
}
protected void addAnnotation(Annotation annotation) {
this.annotations.add(annotation.annotationType(), annotation);
}
public void doWithFields(BiConsumer<String, Field<?, S>> consumer) {
fields.doWithFields(consumer);
}
@Override
public List<TypeInformation<?>> getParameterTypes(Constructor<?> constructor) {
return null;
}
@Nullable
@Override
public TypeInformation<?> getProperty(String property) {
if (!fields.hasField(property)) {
return null;
}
return fields.getField(property).getTypeInformation();
}
@Override
public boolean isCollectionLike() {
return false;
}
@Override
public boolean isMap() {
return false;
}
@Nullable
@Override
public TypeInformation<?> getMapValueType() {
return componentType;
}
@Override
public Class<S> getType() {
return type;
}
@Override
public ClassTypeInformation<?> getRawTypeInformation() {
return this;
}
@Nullable
@Override
public TypeInformation<?> getActualType() {
return componentType != null ? componentType : this;
}
@Override
public TypeInformation<?> getReturnType(Method method) {
return null;
}
@Override
public List<TypeInformation<?>> getParameterTypes(Method method) {
return Collections.emptyList();
}
@Nullable
@Override
public TypeInformation<?> getSuperTypeInformation(Class<?> superType) {
return superTypeInformation;
}
@Override
public boolean isAssignableFrom(TypeInformation<?> target) {
return this.type.isAssignableFrom(target.getType());
}
@Override
public List<TypeInformation<?>> getTypeArguments() {
return typeArguments;
}
@Override
public TypeInformation<? extends S> specialize(ClassTypeInformation<?> type) {
return null;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
if (!super.equals(o))
return false;
DomainTypeInformation<?> that = (DomainTypeInformation<?>) o;
if (!ObjectUtils.nullSafeEquals(type, that.type)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(componentType, that.componentType)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(keyType, that.keyType)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(superTypeInformation, that.superTypeInformation)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(typeArguments, that.typeArguments)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(fields, that.fields)) {
return false;
}
return ObjectUtils.nullSafeEquals(instantiator, that.instantiator);
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + ObjectUtils.nullSafeHashCode(type);
result = 31 * result + ObjectUtils.nullSafeHashCode(componentType);
result = 31 * result + ObjectUtils.nullSafeHashCode(keyType);
result = 31 * result + ObjectUtils.nullSafeHashCode(superTypeInformation);
result = 31 * result + ObjectUtils.nullSafeHashCode(typeArguments);
result = 31 * result + ObjectUtils.nullSafeHashCode(fields);
result = 31 * result + ObjectUtils.nullSafeHashCode(instantiator);
return result;
}
@Nullable
@Override
public EntityInstantiator getEntityInstantiator() {
return instantiator;
}
@Override
public List<Annotation> getAnnotations() {
List<Annotation> all = new ArrayList<>();
annotations.values().forEach(all::addAll);
return all;
}
@Override
public boolean hasAnnotation(Class<?> annotationType) {
return annotations.containsKey(annotationType);
}
@Override
public <T extends Annotation> List<T> findAnnotation(Class<T> annotation) {
return (List<T>) annotations.getOrDefault(annotation, Collections.emptyList());
}
@Nullable
@Override
public PersistentPropertyAccessorFactory getPersistentPropertyAccessorFactory() {
return StaticPropertyAccessorFactory.instance();
}
@Override
public BiFunction<S, Object, S> getSetFunctionFor(String fieldName) {
Field<Object, S> entityField = fields.getField(fieldName);
if (entityField == null) {
return null;
}
return entityField.getSetter();
}
@Override
public Function<S, Object> getGetFunctionFor(String fieldName) {
Field<Object, S> entityField = fields.getField(fieldName);
if (entityField == null) {
return null;
}
return entityField.getGetter();
}
public static class StaticPreferredConstructor extends PreferredConstructor {
private List<String> args;
public StaticPreferredConstructor(List<String> args) {
this.args = args;
}
public static StaticPreferredConstructor of(String... args) {
return new StaticPreferredConstructor(Arrays.asList(args));
}
@Override
public boolean isConstructorParameter(PersistentProperty property) {
if (args.contains(property.getName())) {
return true;
}
return super.isConstructorParameter(property);
}
@Override
public boolean hasParameters() {
return !args.isEmpty();
}
}
}

View File

@@ -1,179 +0,0 @@
/*
* Copyright 2020. 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.
*/
/*
* Copyright 2020 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 java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.springframework.lang.Nullable;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public class Field<T, O> implements AnnotationAware {
@Nullable Class<O> owner;
String propertyName;
TypeInformation<T> typeInformation;
@Nullable TypeInformation<?> componentType;
@Nullable TypeInformation<?> keyType;
MultiValueMap<Class<? extends Annotation>, Annotation> annotations;
@Nullable Function<O, T> getterFunction;
@Nullable BiFunction<O, T, O> setterFunction;
public Field(String propertyName, TypeInformation<T> propertyTypeInformation) {
this.propertyName = propertyName;
this.typeInformation = propertyTypeInformation;
this.annotations = new LinkedMultiValueMap<>();
}
public static <T, O> Field<T, O> simple(Class<T> type, String propertyName) {
if (type == String.class) {
return (Field<T, O>) string(propertyName);
}
throw new IllegalArgumentException("Unknown simple type: " + type);
}
public static <S> Field<String, S> string(String propertyName) {
return new Field<>(propertyName, StringTypeInformation.instance());
}
public static <S> Field<Long, S> int64(String propertyName) {
return new Field<>(propertyName, DomainTypeInformation.from(Long.class));
}
public static <S> Field<Integer, S> int32(String propertyName) {
return new Field<>(propertyName, DomainTypeInformation.from(Integer.class));
}
public static <S, T> Field<T, S> type(String propertyName, TypeInformation<T> type) {
return new Field<>(propertyName, type);
}
public Field<T, O> annotation(Annotation annotation) {
annotations.add(annotation.annotationType(), annotation);
return this;
}
public Field<T, O> wither(BiFunction<O, T, O> setterFunction) {
this.setterFunction = setterFunction;
return this;
}
public Field<T, O> setter(BiConsumer<O, T> setterFunction) {
return wither((o, t) -> {
setterFunction.accept(o, t);
return o;
});
}
public Field<T, O> getter(Function<O, T> getterFunction) {
this.getterFunction = getterFunction;
return this;
}
public Field<T, O> valueType(TypeInformation<?> valueTypeInformation) {
this.componentType = valueTypeInformation;
return this;
}
Field<T, O> owner(Class<O> owner) {
this.owner = owner;
return this;
}
public TypeInformation<?> getValueType() {
return componentType != null ? componentType : typeInformation;
}
public String getFieldName() {
return propertyName;
}
public TypeInformation<T> getTypeInformation() {
return typeInformation;
}
public boolean hasSetter() {
return setterFunction != null;
}
public boolean hasGetter() {
return getterFunction != null;
}
public BiFunction<O, T, O> getSetter() {
return setterFunction;
}
@Nullable
public Function<O, T> getGetter() {
return getterFunction;
}
@Override
public List<Annotation> getAnnotations() {
List<Annotation> all = new ArrayList<>();
annotations.values().forEach(all::addAll);
return all;
}
@Override
public boolean hasAnnotation(Class<?> annotationType) {
return annotations.containsKey(annotationType);
}
@Override
public <T extends Annotation> List<T> findAnnotation(Class<T> annotation) {
return (List<T>) annotations.getOrDefault(annotation, Collections.emptyList());
}
}

View File

@@ -1,76 +0,0 @@
/*
* Copyright 2020. 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.
*/
/*
* Copyright 2020 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 java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.BiConsumer;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public class Fields<O> implements Iterable<Field<?, O>> {
private final Class<O> owner;
private final Map<String, Field<?, O>> fields;
public Fields(Class<O> owner) {
this.owner = owner;
this.fields = new LinkedHashMap<>();
}
public Fields<O> add(Field<?, O> field) {
this.fields.put(field.getFieldName(), field.owner(owner));
return this;
}
public boolean hasField(String fieldName) {
return this.fields.containsKey(fieldName);
}
public <S> Field<S, O> getField(String fieldName) {
return (Field<S, O>) this.fields.get(fieldName);
}
public void doWithFields(BiConsumer<String, Field<?, O>> consumer) {
fields.forEach(consumer);
}
@Override
public Iterator<Field<?, O>> iterator() {
return fields.values().iterator();
}
}

View File

@@ -1,54 +0,0 @@
/*
* Copyright 2020. 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.
*/
/*
* Copyright 2020 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 java.util.List;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public class ListTypeInformation<S> extends DomainTypeInformation<List<S>> {
public ListTypeInformation(TypeInformation<S> componentType) {
super((Class) List.class, componentType, null);
}
public static <S> ListTypeInformation<S> listOf(TypeInformation<S> componentType) {
return new ListTypeInformation<>(componentType);
}
@Override
public boolean isCollectionLike() {
return true;
}
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright 2020. 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.
*/
/*
* Copyright 2020 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;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public class StaticTypeInformationProvider {
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright 2020. 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.
*/
/*
* Copyright 2020 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;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public class StringTypeInformation extends DomainTypeInformation<String> {
private static final StringTypeInformation INSTANCE = new StringTypeInformation();
public StringTypeInformation() {
super(String.class);
}
public static TypeInformation<String> instance() {
return INSTANCE;
}
}

View File

@@ -1,672 +0,0 @@
/*
* Copyright 2011-2020 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.util;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.springframework.beans.BeanUtils;
import org.springframework.core.GenericTypeResolver;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
/**
* Basic {@link TypeDiscoverer} that contains basic functionality to discover property types.
*
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
*/
class TypeDiscoverer<S> implements TypeInformation<S> {
private static final Class<?>[] MAP_TYPES;
static {
ClassLoader classLoader = TypeDiscoverer.class.getClassLoader();
Set<Class<?>> mapTypes = new HashSet<>();
mapTypes.add(Map.class);
try {
mapTypes.add(ClassUtils.forName("io.vavr.collection.Map", classLoader));
} catch (ClassNotFoundException o_O) {}
MAP_TYPES = mapTypes.toArray(new Class[0]);
}
private final Type type;
private final Map<TypeVariable<?>, Type> typeVariableMap;
private final Map<String, Optional<TypeInformation<?>>> fieldTypes = new ConcurrentHashMap<>();
private final int hashCode;
private final Lazy<Class<S>> resolvedType;
private final Lazy<TypeInformation<?>> componentType;
private final Lazy<TypeInformation<?>> valueType;
/**
* Creates a new {@link TypeDiscoverer} for the given type, type variable map and parent.
*
* @param type must not be {@literal null}.
* @param typeVariableMap must not be {@literal null}.
*/
protected TypeDiscoverer(Type type, Map<TypeVariable<?>, Type> typeVariableMap) {
Assert.notNull(type, "Type must not be null!");
Assert.notNull(typeVariableMap, "TypeVariableMap must not be null!");
this.type = type;
this.resolvedType = Lazy.of(() -> resolveType(type));
this.componentType = Lazy.of(this::doGetComponentType);
this.valueType = Lazy.of(this::doGetMapValueType);
this.typeVariableMap = typeVariableMap;
this.hashCode = 17 + 31 * type.hashCode() + 31 * typeVariableMap.hashCode();
}
protected TypeDiscoverer(Class<?> type, TypeInformation<?> componentType, TypeInformation<?> keyType) {
this.type = null;
this.typeVariableMap = Collections.emptyMap();
this.hashCode = 17 + 31 * type.hashCode();
this.resolvedType = Lazy.of((Class<S>) type);
this.componentType = componentType == null ? Lazy.empty() : Lazy.of(componentType);
this.valueType = keyType == null ? Lazy.empty() : Lazy.of(keyType);
}
/**
* Returns the type variable map.
*
* @return
*/
protected Map<TypeVariable<?>, Type> getTypeVariableMap() {
return typeVariableMap;
}
/**
* Creates {@link TypeInformation} for the given {@link Type}.
*
* @param fieldType must not be {@literal null}.
* @return
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected TypeInformation<?> createInfo(Type fieldType) {
Assert.notNull(fieldType, "Field type must not be null!");
if (fieldType.equals(this.type)) {
return this;
}
if (fieldType instanceof Class) {
return ClassTypeInformation.from((Class<?>) fieldType);
}
if (fieldType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) fieldType;
return new ParameterizedTypeInformation(parameterizedType, this);
}
if (fieldType instanceof TypeVariable) {
TypeVariable<?> variable = (TypeVariable<?>) fieldType;
return new TypeVariableTypeInformation(variable, this);
}
if (fieldType instanceof GenericArrayType) {
return new GenericArrayTypeInformation((GenericArrayType) fieldType, this);
}
if (fieldType instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) fieldType;
Type[] bounds = wildcardType.getLowerBounds();
if (bounds.length > 0) {
return createInfo(bounds[0]);
}
bounds = wildcardType.getUpperBounds();
if (bounds.length > 0) {
return createInfo(bounds[0]);
}
}
throw new IllegalArgumentException();
}
/**
* Resolves the given type into a plain {@link Class}.
*
* @param type
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
protected Class<S> resolveType(Type type) {
Map<TypeVariable, Type> map = new HashMap<>();
map.putAll(getTypeVariableMap());
return (Class<S>) GenericTypeResolver.resolveType(type, map);
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getParameterTypes(java.lang.reflect.Constructor)
*/
public List<TypeInformation<?>> getParameterTypes(Constructor<?> constructor) {
Assert.notNull(constructor, "Constructor must not be null!");
Type[] types = constructor.getGenericParameterTypes();
List<TypeInformation<?>> result = new ArrayList<>(types.length);
for (Type parameterType : types) {
result.add(createInfo(parameterType));
}
return result;
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getProperty(java.lang.String)
*/
@Nullable
public TypeInformation<?> getProperty(String fieldname) {
int separatorIndex = fieldname.indexOf('.');
if (separatorIndex == -1) {
return fieldTypes.computeIfAbsent(fieldname, this::getPropertyInformation).orElse(null);
}
String head = fieldname.substring(0, separatorIndex);
TypeInformation<?> info = getProperty(head);
if (info == null) {
return null;
}
return info.getProperty(fieldname.substring(separatorIndex + 1));
}
/**
* Returns the {@link TypeInformation} for the given atomic field. Will inspect fields first and return the type of a
* field if available. Otherwise it will fall back to a {@link PropertyDescriptor}.
*
* @see #getGenericType(PropertyDescriptor)
* @param fieldname
* @return
*/
@SuppressWarnings("null")
private Optional<TypeInformation<?>> getPropertyInformation(String fieldname) {
Class<?> rawType = getType();
Field field = ReflectionUtils.findField(rawType, fieldname);
if (field != null) {
return Optional.of(createInfo(field.getGenericType()));
}
return findPropertyDescriptor(rawType, fieldname).map(it -> createInfo(getGenericType(it)));
}
/**
* Finds the {@link PropertyDescriptor} for the property with the given name on the given type.
*
* @param type must not be {@literal null}.
* @param fieldname must not be {@literal null} or empty.
* @return
*/
private static Optional<PropertyDescriptor> findPropertyDescriptor(Class<?> type, String fieldname) {
PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(type, fieldname);
if (descriptor != null) {
return Optional.of(descriptor);
}
List<Class<?>> superTypes = new ArrayList<>();
superTypes.addAll(Arrays.asList(type.getInterfaces()));
superTypes.add(type.getSuperclass());
return Streamable.of(type.getInterfaces()).stream()//
.flatMap(it -> Optionals.toStream(findPropertyDescriptor(it, fieldname)))//
.findFirst();
}
/**
* Returns the generic type for the given {@link PropertyDescriptor}. Will inspect its read method followed by the
* first parameter of the write method.
*
* @param descriptor must not be {@literal null}
* @return
*/
@Nullable
private static Type getGenericType(PropertyDescriptor descriptor) {
Method method = descriptor.getReadMethod();
if (method != null) {
return method.getGenericReturnType();
}
method = descriptor.getWriteMethod();
if (method == null) {
return null;
}
Type[] parameterTypes = method.getGenericParameterTypes();
return parameterTypes.length == 0 ? null : parameterTypes[0];
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getType()
*/
public Class<S> getType() {
return resolvedType.get();
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getRawTypeInformation()
*/
@Override
public ClassTypeInformation<?> getRawTypeInformation() {
return ClassTypeInformation.from(getType()).getRawTypeInformation();
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getActualType()
*/
@Nullable
public TypeInformation<?> getActualType() {
if (isMap()) {
return getMapValueType();
}
if (isCollectionLike()) {
return getComponentType();
}
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#isMap()
*/
public boolean isMap() {
Class<S> type = getType();
for (Class<?> mapType : MAP_TYPES) {
if (mapType.isAssignableFrom(type)) {
return true;
}
}
return false;
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getMapValueType()
*/
@Nullable
public TypeInformation<?> getMapValueType() {
return valueType.orElse(null);
}
@Nullable
protected TypeInformation<?> doGetMapValueType() {
return isMap() ? getTypeArgument(getBaseType(MAP_TYPES), 1)
: getTypeArguments().stream().skip(1).findFirst().orElse(null);
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#isCollectionLike()
*/
public boolean isCollectionLike() {
Class<?> rawType = getType();
return rawType.isArray() //
|| Iterable.class.equals(rawType) //
|| Collection.class.isAssignableFrom(rawType) //
|| Streamable.class.isAssignableFrom(rawType);
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getComponentType()
*/
@Nullable
public final TypeInformation<?> getComponentType() {
return componentType.orElse(null);
}
@Nullable
protected TypeInformation<?> doGetComponentType() {
Class<S> rawType = getType();
if (rawType.isArray()) {
return createInfo(rawType.getComponentType());
}
if (isMap()) {
return getTypeArgument(getBaseType(MAP_TYPES), 0);
}
if (Iterable.class.isAssignableFrom(rawType)) {
return getTypeArgument(Iterable.class, 0);
}
List<TypeInformation<?>> arguments = getTypeArguments();
return arguments.size() > 0 ? arguments.get(0) : null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getReturnType(java.lang.reflect.Method)
*/
public TypeInformation<?> getReturnType(Method method) {
Assert.notNull(method, "Method must not be null!");
return createInfo(method.getGenericReturnType());
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getMethodParameterTypes(java.lang.reflect.Method)
*/
public List<TypeInformation<?>> getParameterTypes(Method method) {
Assert.notNull(method, "Method most not be null!");
return Streamable.of(method.getGenericParameterTypes()).stream()//
.map(this::createInfo)//
.collect(Collectors.toList());
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getSuperTypeInformation(java.lang.Class)
*/
@Nullable
public TypeInformation<?> getSuperTypeInformation(Class<?> superType) {
Class<?> rawType = getType();
if (!superType.isAssignableFrom(rawType)) {
return null;
}
if (getType().equals(superType)) {
return this;
}
List<Type> candidates = new ArrayList<>();
Type genericSuperclass = rawType.getGenericSuperclass();
if (genericSuperclass != null) {
candidates.add(genericSuperclass);
}
candidates.addAll(Arrays.asList(rawType.getGenericInterfaces()));
for (Type candidate : candidates) {
TypeInformation<?> candidateInfo = createInfo(candidate);
if (superType.equals(candidateInfo.getType())) {
return candidateInfo;
} else {
TypeInformation<?> nestedSuperType = candidateInfo.getSuperTypeInformation(superType);
if (nestedSuperType != null) {
return nestedSuperType;
}
}
}
return null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getTypeParameters()
*/
public List<TypeInformation<?>> getTypeArguments() {
return Collections.emptyList();
}
/* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#isAssignableFrom(org.springframework.data.util.TypeInformation)
*/
public boolean isAssignableFrom(TypeInformation<?> target) {
TypeInformation<?> superTypeInformation = target.getSuperTypeInformation(getType());
return superTypeInformation == null ? false : superTypeInformation.equals(this);
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#specialize(org.springframework.data.util.ClassTypeInformation)
*/
@Override
@SuppressWarnings("unchecked")
public TypeInformation<? extends S> specialize(ClassTypeInformation<?> type) {
Assert.notNull(type, "Type must not be null!");
Assert.isTrue(getType().isAssignableFrom(type.getType()),
() -> String.format("%s must be assignable from %s", getType(), type.getType()));
List<TypeInformation<?>> typeArguments = getTypeArguments();
return (TypeInformation<? extends S>) (typeArguments.isEmpty() //
? type //
: type.createInfo(new SyntheticParamterizedType(type, getTypeArguments())));
}
@Nullable
private TypeInformation<?> getTypeArgument(Class<?> bound, int index) {
Class<?>[] arguments = GenericTypeResolver.resolveTypeArguments(getType(), bound);
if (arguments != null) {
return createInfo(arguments[index]);
}
return getSuperTypeInformation(bound) instanceof ParameterizedTypeInformation //
? ClassTypeInformation.OBJECT //
: null;
}
private Class<?> getBaseType(Class<?>[] candidates) {
Class<S> type = getType();
for (Class<?> candidate : candidates) {
if (candidate.isAssignableFrom(type)) {
return candidate;
}
}
throw new IllegalArgumentException(String.format("Type %s not contained in candidates %s!", type, candidates));
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(@Nullable Object obj) {
if (obj == this) {
return true;
}
if (obj == null) {
return false;
}
if (!this.getClass().equals(obj.getClass())) {
return false;
}
TypeDiscoverer<?> that = (TypeDiscoverer<?>) obj;
if (!this.type.equals(that.type)) {
return false;
}
if (this.typeVariableMap.isEmpty() && that.typeVariableMap.isEmpty()) {
return true;
}
return this.typeVariableMap.equals(that.typeVariableMap);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return hashCode;
}
/**
* A synthetic {@link ParameterizedType}.
*
* @author Oliver Gierke
* @since 1.11
*/
private static class SyntheticParamterizedType implements ParameterizedType {
private final ClassTypeInformation<?> typeInformation;
private final List<TypeInformation<?>> typeParameters;
public SyntheticParamterizedType(ClassTypeInformation<?> typeInformation, List<TypeInformation<?>> typeParameters) {
this.typeInformation = typeInformation;
this.typeParameters = typeParameters;
}
/*
* (non-Javadoc)
* @see java.lang.reflect.ParameterizedType#getRawType()
*/
@Override
public Type getRawType() {
return typeInformation.getType();
}
/*
* (non-Javadoc)
* @see java.lang.reflect.ParameterizedType#getOwnerType()
*/
@Override
@Nullable
public Type getOwnerType() {
return null;
}
/*
* (non-Javadoc)
* @see java.lang.reflect.ParameterizedType#getActualTypeArguments()
*/
@Override
public Type[] getActualTypeArguments() {
Type[] result = new Type[typeParameters.size()];
for (int i = 0; i < typeParameters.size(); i++) {
result[i] = typeParameters.get(i).getType();
}
return result;
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.ParentTypeAwareTypeInformation#equals(java.lang.Object)
*/
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (!(o instanceof SyntheticParamterizedType)) {
return false;
}
SyntheticParamterizedType that = (SyntheticParamterizedType) o;
if (!ObjectUtils.nullSafeEquals(typeInformation, that.typeInformation)) {
return false;
}
return ObjectUtils.nullSafeEquals(typeParameters, that.typeParameters);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(typeInformation);
result = 31 * result + ObjectUtils.nullSafeHashCode(typeParameters);
return result;
}
}
}

View File

@@ -1,19 +1,3 @@
/*
* Copyright 2020. 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.
*/
/*
* Copyright 2020 the original author or authors.
*
@@ -21,7 +5,7 @@
* 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
* 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,
@@ -29,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.util;
package org.springframework.data.mongodb.buildtimetypeinfo;
import org.springframework.util.ObjectUtils;
@@ -55,14 +39,6 @@ public class Address {
return street;
}
// public void setCity(String city) {
// this.city = city;
// }
//
// public void setStreet(String street) {
// this.street = street;
// }
@Override
public String toString() {
return "Address{" + "city='" + city + '\'' + ", street='" + street + '\'' + '}';

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2020 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.mongodb.buildtimetypeinfo;
import org.springframework.data.mapping.model.DomainTypeConstructor;
import org.springframework.data.mapping.model.DomainTypeInformation;
import org.springframework.data.mapping.model.Field;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public class AddressTypeInformation extends DomainTypeInformation<Address> {
private static final AddressTypeInformation INSTANCE = new AddressTypeInformation();
private AddressTypeInformation() {
super(Address.class);
// CONSTRUCTOR
setConstructor(computePreferredConstructor());
// FIELDS
addField(Field.<Address> string("city").getter(Address::getCity));
addField(Field.<Address> string("street").getter(Address::getStreet));
}
public static AddressTypeInformation instance() {
return INSTANCE;
}
private DomainTypeConstructor<Address> computePreferredConstructor() {
return DomainTypeConstructor.<Address> builder().args("city", "street")
.newInstanceFunction(args -> new Address((String) args[0], (String) args[1]));
}
}

View File

@@ -1,19 +1,3 @@
/*
* Copyright 2020. 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.
*/
/*
* Copyright 2020 the original author or authors.
*
@@ -21,7 +5,7 @@
* 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
* 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,
@@ -29,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.util;
package org.springframework.data.mongodb.buildtimetypeinfo;
import java.util.List;

View File

@@ -1,19 +1,3 @@
/*
* Copyright 2020. 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.
*/
/*
* Copyright 2020 the original author or authors.
*
@@ -21,7 +5,7 @@
* 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
* 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,
@@ -29,18 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.util;
package org.springframework.data.mongodb.buildtimetypeinfo;
import java.lang.annotation.Annotation;
import java.util.List;
import org.springframework.data.annotation.Id;
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.EntityInstantiator;
import org.springframework.data.mapping.model.ParameterValueProvider;
import org.springframework.data.mapping.model.DomainTypeConstructor;
import org.springframework.data.mapping.model.DomainTypeInformation;
import org.springframework.data.mapping.model.Field;
import org.springframework.data.mapping.model.ListTypeInformation;
import org.springframework.data.mapping.model.StringTypeInformation;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.FieldType;
@@ -53,23 +35,18 @@ public class PersonTypeInformation extends DomainTypeInformation<Person> {
private static final PersonTypeInformation INSTANCE = new PersonTypeInformation();
private PersonTypeInformation() {
super(Person.class);
}
public static PersonTypeInformation instance() {
return INSTANCE;
}
// CONSTRUCTOR
setConstructor(computePreferredConstructor());
@Override
protected void computeFields() {
// ANNOTATIONS
addAnnotation(computeAtDocumentAnnotation());
// FIELDS
addField(
Field.<Person> int64("id").getter(Person::getId).wither((bean, id) -> bean.withId(id)).annotation(new Id() {
@Override
public Class<? extends Annotation> annotationType() {
return Id.class;
}
}));
Field.<Person> int64("id").annotatedWithAtId().getter(Person::getId).wither((bean, id) -> bean.withId(id)));
addField(Field.<Person> string("firstname").getter(Person::getFirstname).annotation(atFieldOnFirstname()));
addField(Field.<Person> string("lastname").getter(Person::getLastname));
addField(Field.<Person> int32("age").getter(Person::getAge).setter(Person::setAge));
@@ -77,11 +54,20 @@ public class PersonTypeInformation extends DomainTypeInformation<Person> {
.setter(Person::setAddress));
addField(Field.<Person, List<String>> type("nicknames", new ListTypeInformation<>(StringTypeInformation.instance()))
.getter(Person::getNicknames).setter(Person::setNicknames));
}
@Override
protected void computeAnnotations() {
addAnnotation(new Document() {
public static PersonTypeInformation instance() {
return INSTANCE;
}
private DomainTypeConstructor<Person> computePreferredConstructor() {
return DomainTypeConstructor.<Person> builder().args("firstname", "lastname")
.newInstanceFunction((args) -> new Person((String) args[0], (String) args[1]));
}
private Document computeAtDocumentAnnotation() {
return new Document() {
@Override
public Class<? extends Annotation> annotationType() {
return Document.class;
@@ -106,37 +92,10 @@ public class PersonTypeInformation extends DomainTypeInformation<Person> {
public String collation() {
return "";
}
});
}
@Override
protected EntityInstantiator computeEntityInstantiator() {
return new EntityInstantiator() {
@Override
public <T, E extends PersistentEntity<? extends T, P>, P extends PersistentProperty<P>> T createInstance(E entity,
ParameterValueProvider<P> provider) {
String firstname = (String) provider.getParameterValue(
new Parameter("firstname", StringTypeInformation.instance(), new Annotation[] {}, entity));
String lastname = (String) provider.getParameterValue(
new Parameter("lastname", StringTypeInformation.instance(), new Annotation[] {}, entity));
T person = (T) new Person(firstname, lastname);
System.out.println("Created new Person instance via constructor using values (" + firstname + ", " + lastname
+ ") resulting in " + person);
return person;
}
};
}
@Override
protected PreferredConstructor computePreferredConstructor() {
return StaticPreferredConstructor.of("firstname", "lastname");
}
Annotation atFieldOnFirstname() {
private Annotation atFieldOnFirstname() {
return new org.springframework.data.mongodb.core.mapping.Field() {

View File

@@ -80,12 +80,10 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.mapping.PersonPojoStringId;
import org.springframework.data.mongodb.core.mapping.TextScore;
import org.springframework.data.mongodb.core.mapping.event.AfterConvertCallback;
import org.springframework.data.util.AddressTypeInformation;
import org.springframework.data.mongodb.buildtimetypeinfo.AddressTypeInformation;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.Person;
import org.springframework.data.util.PersonTypeInformation;
import org.springframework.data.mongodb.buildtimetypeinfo.PersonTypeInformation;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.StopWatch;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
@@ -2192,13 +2190,13 @@ public class MappingMongoConverterUnitTests {
//
// MongoMappingContext mappingContext = new MongoMappingContext();
// mappingContext.setInitialEntitySet(new LinkedHashSet<>(
// Arrays.asList(org.springframework.data.util.Person.class, org.springframework.data.util.Address.class)));
// Arrays.asList(org.springframework.data.mongodb.xxx.Person.class, org.springframework.data.mongodb.xxx.Address.class)));
// mappingContext.initialize();
//
// MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext);
//
// org.springframework.data.util.Person source = new org.springframework.data.util.Person("spring", "data");
// source.setAddress(new org.springframework.data.util.Address("the city", "never sleeps"));
// org.springframework.data.mongodb.xxx.Person source = new org.springframework.data.mongodb.xxx.Person("spring", "data");
// source.setAddress(new org.springframework.data.mongodb.xxx.Address("the city", "never sleeps"));
// source.setAge(10);
// source = source.withId(9876);
// source.setNicknames(Arrays.asList("tick", "trick", "track"));
@@ -2218,7 +2216,7 @@ public class MappingMongoConverterUnitTests {
//
// stopWatch.start("read");
// for (org.bson.Document sourceDoc : sources) {
// assertThat(converter.read(org.springframework.data.util.Person.class, sourceDoc)).isEqualTo(source);
// assertThat(converter.read(org.springframework.data.mongodb.xxx.Person.class, sourceDoc)).isEqualTo(source);
// }
// stopWatch.stop();
//
@@ -2231,14 +2229,14 @@ public class MappingMongoConverterUnitTests {
// ClassTypeInformation.warmCache(new PersonTypeInformation(), new AddressTypeInformation());
//
// MongoMappingContext mappingContext = new MongoMappingContext();
// mappingContext.setInitialEntitySet(new LinkedHashSet<>(Arrays.asList(org.springframework.data.util.Person.class,
// org.springframework.data.util.Address.class)));
// mappingContext.setInitialEntitySet(new LinkedHashSet<>(Arrays.asList(org.springframework.data.mongodb.xxx.Person.class,
// org.springframework.data.mongodb.xxx.Address.class)));
// mappingContext.initialize();
//
// MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext);
//
// org.springframework.data.util.Person source = new org.springframework.data.util.Person("spring", "data");
// source.setAddress(new org.springframework.data.util.Address("the city", "never sleeps"));
// org.springframework.data.mongodb.xxx.Person source = new org.springframework.data.mongodb.xxx.Person("spring", "data");
// source.setAddress(new org.springframework.data.mongodb.xxx.Address("the city", "never sleeps"));
// source.setAge(10);
// source.setId(9876);
// source.setNicknames(Arrays.asList("tick", "trick", "track"));
@@ -2252,11 +2250,11 @@ public class MappingMongoConverterUnitTests {
MongoMappingContext mappingContext = new MongoMappingContext();
mappingContext.setInitialEntitySet(new LinkedHashSet<>(
Arrays.asList(org.springframework.data.util.Person.class, org.springframework.data.util.Address.class)));
Arrays.asList(org.springframework.data.mongodb.buildtimetypeinfo.Person.class, org.springframework.data.mongodb.buildtimetypeinfo.Address.class)));
mappingContext.initialize();
org.springframework.data.util.Person source = new org.springframework.data.util.Person("spring", "data");
source.setAddress(new org.springframework.data.util.Address("the city", "never sleeps"));
org.springframework.data.mongodb.buildtimetypeinfo.Person source = new org.springframework.data.mongodb.buildtimetypeinfo.Person("spring", "data");
source.setAddress(new org.springframework.data.mongodb.buildtimetypeinfo.Address("the city", "never sleeps"));
source.setAge(10);
source = source.withId(9876);
source.setNicknames(Arrays.asList("tick", "trick", "track"));
@@ -2277,13 +2275,13 @@ public class MappingMongoConverterUnitTests {
new org.bson.Document("city", "the city").append("street", "never sleeps"));
assertThat(targetDocument).containsEntry("nicknames", Arrays.asList("tick", "trick", "track"));
org.springframework.data.util.Person targetEntity = converter.read(org.springframework.data.util.Person.class,
org.springframework.data.mongodb.buildtimetypeinfo.Person targetEntity = converter.read(org.springframework.data.mongodb.buildtimetypeinfo.Person.class,
targetDocument);
System.out.println();
System.out.println("targetEntity: " + targetEntity);
assertThat(targetEntity).isEqualTo(source);
BasicMongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(org.springframework.data.util.Person.class);
BasicMongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(org.springframework.data.mongodb.buildtimetypeinfo.Person.class);
assertThat(entity.getCollection()).isEqualTo("star-wars");
}

View File

@@ -47,10 +47,11 @@ import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.util.AddressTypeInformation;
import org.springframework.data.mongodb.buildtimetypeinfo.Address;
import org.springframework.data.mongodb.buildtimetypeinfo.AddressTypeInformation;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.Person;
import org.springframework.data.util.PersonTypeInformation;
import org.springframework.data.mongodb.buildtimetypeinfo.Person;
import org.springframework.data.mongodb.buildtimetypeinfo.PersonTypeInformation;
import com.mongodb.client.MongoClients;
@@ -76,7 +77,7 @@ public class StaticMetadataTests {
mappingContext = new MongoMappingContext();
mappingContext.setInitialEntitySet(new LinkedHashSet<>(
Arrays.asList(org.springframework.data.util.Person.class, org.springframework.data.util.Address.class)));
Arrays.asList(Person.class, Address.class)));
mappingContext.initialize();
mongoConverter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext);
@@ -85,7 +86,7 @@ public class StaticMetadataTests {
template = new MongoTemplate(new SimpleMongoClientDatabaseFactory(MongoClients.create(), "sem"), mongoConverter);
luke = new Person("luke", "skywalker");
luke.setAddress(new org.springframework.data.util.Address("Mos Eisley", "WB154"));
luke.setAddress(new Address("Mos Eisley", "WB154"));
luke.setAge(22);
luke = luke.withId(9876);
luke.setNicknames(Arrays.asList("jedi", "wormie"));