/*
 * Copyright 2017-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.repository.core.support;

import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Stream;

import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

Value object representing a repository fragment.

Repository fragments are individual parts that contribute method signatures. They are used to form a RepositoryComposition. Fragments can be purely structural or backed with an implementation.

Structural fragments are not backed by an implementation and are primarily used to discover the structure of a repository composition and to perform validations.

Implemented repository fragments consist of a signature contributor and the implementing object. A signature contributor may be an interface or just the implementing object providing the available signatures for a repository.

Fragments are immutable.
Author:Mark Paluch
See Also:
Since:2.0
/** * Value object representing a repository fragment. * <p /> * Repository fragments are individual parts that contribute method signatures. They are used to form a * {@link RepositoryComposition}. Fragments can be purely structural or backed with an implementation. * <p/> * {@link #structural(Class) Structural} fragments are not backed by an implementation and are primarily used to * discover the structure of a repository composition and to perform validations. * <p/> * {@link #implemented(Object) Implemented} repository fragments consist of a signature contributor and the implementing * object. A signature contributor may be {@link #implemented(Class, Object) an interface} or * {@link #implemented(Object) just the implementing object} providing the available signatures for a repository. * <p/> * Fragments are immutable. * * @author Mark Paluch * @since 2.0 * @see RepositoryComposition */
public interface RepositoryFragment<T> {
Create an implemented RepositoryFragment backed by the implementation object.
Params:
  • implementation – must not be null.
Returns:
/** * Create an implemented {@link RepositoryFragment} backed by the {@code implementation} object. * * @param implementation must not be {@literal null}. * @return */
public static <T> RepositoryFragment<T> implemented(T implementation) { return new ImplementedRepositoryFragment<T>(Optional.empty(), implementation); }
Create an implemented RepositoryFragment from a interfaceClass backed by the implementation object.
Params:
  • implementation – must not be null.
Returns:
/** * Create an implemented {@link RepositoryFragment} from a {@code interfaceClass} backed by the {@code implementation} * object. * * @param implementation must not be {@literal null}. * @return */
public static <T> RepositoryFragment<T> implemented(Class<T> interfaceClass, T implementation) { return new ImplementedRepositoryFragment<>(Optional.of(interfaceClass), implementation); }
Create a structural RepositoryFragment given interfaceOrImplementation.
Params:
  • interfaceOrImplementation – must not be null.
Returns:
/** * Create a structural {@link RepositoryFragment} given {@code interfaceOrImplementation}. * * @param interfaceOrImplementation must not be {@literal null}. * @return */
public static <T> RepositoryFragment<T> structural(Class<T> interfaceOrImplementation) { return new StructuralRepositoryFragment<>(interfaceOrImplementation); }
Attempt to find the Method by name and exact parameters. Returns true if the method was found or false otherwise.
Params:
  • method – must not be null.
Returns:true if the method was found or false otherwise
/** * Attempt to find the {@link Method} by name and exact parameters. Returns {@literal true} if the method was found or * {@literal false} otherwise. * * @param method must not be {@literal null}. * @return {@literal true} if the method was found or {@literal false} otherwise */
default boolean hasMethod(Method method) { Assert.notNull(method, "Method must not be null!"); return ReflectionUtils.findMethod(getSignatureContributor(), method.getName(), method.getParameterTypes()) != null; }
Returns:the optional implementation. Only available for implemented fragments. Structural fragments return always Optional.empty().
/** * @return the optional implementation. Only available for implemented fragments. Structural fragments return always * {@link Optional#empty()}. */
default Optional<T> getImplementation() { return Optional.empty(); }
Returns:a Stream of methods exposed by this RepositoryFragment.
/** * @return a {@link Stream} of methods exposed by this {@link RepositoryFragment}. */
default Stream<Method> methods() { return Arrays.stream(getSignatureContributor().getMethods()); }
Returns:the class/interface providing signatures for this RepositoryFragment.
/** * @return the class/interface providing signatures for this {@link RepositoryFragment}. */
Class<?> getSignatureContributor();
Implement a structural RepositoryFragment given its implementation object. Returns an implemented RepositoryFragment.
Params:
  • implementation – must not be null.
Returns:a new implemented RepositoryFragment for implementation.
/** * Implement a structural {@link RepositoryFragment} given its {@code implementation} object. Returns an implemented * {@link RepositoryFragment}. * * @param implementation must not be {@literal null}. * @return a new implemented {@link RepositoryFragment} for {@code implementation}. */
RepositoryFragment<T> withImplementation(T implementation); @RequiredArgsConstructor @EqualsAndHashCode(callSuper = false) static class StructuralRepositoryFragment<T> implements RepositoryFragment<T> { private final @NonNull Class<T> interfaceOrImplementation; /* * (non-Javadoc) * @see org.springframework.data.repository.core.support.RepositoryFragment#getSignatureContributor() */ @Override public Class<?> getSignatureContributor() { return interfaceOrImplementation; } /* * (non-Javadoc) * @see org.springframework.data.repository.core.support.RepositoryFragment#withImplementation(java.lang.Object) */ @Override public RepositoryFragment<T> withImplementation(T implementation) { return new ImplementedRepositoryFragment<>(Optional.of(interfaceOrImplementation), implementation); } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return String.format("StructuralRepositoryFragment %s", ClassUtils.getShortName(interfaceOrImplementation)); } } @EqualsAndHashCode(callSuper = false) static class ImplementedRepositoryFragment<T> implements RepositoryFragment<T> { private final Optional<Class<T>> interfaceClass; private final T implementation; private final Optional<T> optionalImplementation;
Creates a new ImplementedRepositoryFragment for the given interface class and implementation.
Params:
  • interfaceClass – must not be null.
  • implementation – must not be null.
/** * Creates a new {@link ImplementedRepositoryFragment} for the given interface class and implementation. * * @param interfaceClass must not be {@literal null}. * @param implementation must not be {@literal null}. */
public ImplementedRepositoryFragment(Optional<Class<T>> interfaceClass, T implementation) { Assert.notNull(interfaceClass, "Interface class must not be null!"); Assert.notNull(implementation, "Implementation object must not be null!"); interfaceClass.ifPresent(it -> { Assert.isTrue(ClassUtils.isAssignableValue(it, implementation), () -> String.format("Fragment implementation %s does not implement %s!", ClassUtils.getQualifiedName(implementation.getClass()), ClassUtils.getQualifiedName(it))); }); this.interfaceClass = interfaceClass; this.implementation = implementation; this.optionalImplementation = Optional.of(implementation); } /* * (non-Javadoc) * @see org.springframework.data.repository.core.support.RepositoryFragment#getSignatureContributor() */ @SuppressWarnings({ "rawtypes", "unchecked" }) public Class<?> getSignatureContributor() { return interfaceClass.orElse((Class) implementation.getClass()); } /* * (non-Javadoc) * @see org.springframework.data.repository.core.support.RepositoryFragment#getImplementation() */ @Override public Optional<T> getImplementation() { return optionalImplementation; } /* * (non-Javadoc) * @see org.springframework.data.repository.core.support.RepositoryFragment#withImplementation(java.lang.Object) */ @Override public RepositoryFragment<T> withImplementation(T implementation) { return new ImplementedRepositoryFragment<>(interfaceClass, implementation); } /* * (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return String.format("ImplementedRepositoryFragment %s%s", interfaceClass.map(ClassUtils::getShortName).map(it -> it + ":").orElse(""), ClassUtils.getShortName(implementation.getClass())); } } }