1 /*
2  * Copyright 2011-2020 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.springframework.data.repository.core.support;
17
18 import java.lang.reflect.Method;
19 import java.util.Arrays;
20 import java.util.Collections;
21 import java.util.List;
22 import java.util.Set;
23
24 import org.springframework.core.KotlinDetector;
25 import org.springframework.data.domain.Pageable;
26 import org.springframework.data.repository.Repository;
27 import org.springframework.data.repository.core.CrudMethods;
28 import org.springframework.data.repository.core.RepositoryMetadata;
29 import org.springframework.data.repository.util.QueryExecutionConverters;
30 import org.springframework.data.repository.util.ReactiveWrappers;
31 import org.springframework.data.util.ClassTypeInformation;
32 import org.springframework.data.util.KotlinReflectionUtils;
33 import org.springframework.data.util.Lazy;
34 import org.springframework.data.util.TypeInformation;
35 import org.springframework.util.Assert;
36
37 /**
38  * Base class for {@link RepositoryMetadata} implementations.
39  *
40  * @author Oliver Gierke
41  * @author Thomas Darimont
42  * @author Jens Schauder
43  * @author Mark Paluch
44  */

45 public abstract class AbstractRepositoryMetadata implements RepositoryMetadata {
46
47     private final TypeInformation<?> typeInformation;
48     private final Class<?> repositoryInterface;
49     private final Lazy<CrudMethods> crudMethods;
50
51     /**
52      * Creates a new {@link AbstractRepositoryMetadata}.
53      *
54      * @param repositoryInterface must not be {@literal null} and must be an interface.
55      */

56     public AbstractRepositoryMetadata(Class<?> repositoryInterface) {
57
58         Assert.notNull(repositoryInterface, "Given type must not be null!");
59         Assert.isTrue(repositoryInterface.isInterface(), "Given type must be an interface!");
60
61         this.repositoryInterface = repositoryInterface;
62         this.typeInformation = ClassTypeInformation.from(repositoryInterface);
63         this.crudMethods = Lazy.of(() -> new DefaultCrudMethods(this));
64     }
65
66     /**
67      * Creates a new {@link RepositoryMetadata} for the given repository interface.
68      *
69      * @param repositoryInterface must not be {@literal null}.
70      * @since 1.9
71      * @return
72      */

73     public static RepositoryMetadata getMetadata(Class<?> repositoryInterface) {
74
75         Assert.notNull(repositoryInterface, "Repository interface must not be null!");
76
77         return Repository.class.isAssignableFrom(repositoryInterface) ? new DefaultRepositoryMetadata(repositoryInterface)
78                 : new AnnotationRepositoryMetadata(repositoryInterface);
79     }
80
81     /*
82      * (non-Javadoc)
83      * @see org.springframework.data.repository.core.RepositoryMetadata#getReturnType(java.lang.reflect.Method)
84      */

85     @Override
86     public TypeInformation<?> getReturnType(Method method) {
87
88         TypeInformation<?> returnType = null;
89         if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinReflectionUtils.isSuspend(method)) {
90
91             // the last parameter is Continuation<? super T> or Continuation<? super Flow<? super T>>
92             List<TypeInformation<?>> types = typeInformation.getParameterTypes(method);
93             returnType = types.get(types.size() - 1).getComponentType();
94         }
95
96         if (returnType == null) {
97             returnType = typeInformation.getReturnType(method);
98         }
99
100         return returnType;
101     }
102
103     /*
104      * (non-Javadoc)
105      * @see org.springframework.data.repository.core.RepositoryMetadata#getReturnedDomainClass(java.lang.reflect.Method)
106      */

107     public Class<?> getReturnedDomainClass(Method method) {
108         return QueryExecutionConverters.unwrapWrapperTypes(getReturnType(method)).getType();
109     }
110
111     /*
112      * (non-Javadoc)
113      * @see org.springframework.data.repository.core.RepositoryMetadata#getRepositoryInterface()
114      */

115     public Class<?> getRepositoryInterface() {
116         return this.repositoryInterface;
117     }
118
119     /*
120      * (non-Javadoc)
121      * @see org.springframework.data.repository.core.RepositoryMetadata#getCrudMethods()
122      */

123     @Override
124     public CrudMethods getCrudMethods() {
125         return this.crudMethods.get();
126     }
127
128     /*
129      * (non-Javadoc)
130      * @see org.springframework.data.repository.core.RepositoryMetadata#isPagingRepository()
131      */

132     @Override
133     public boolean isPagingRepository() {
134
135         return getCrudMethods().getFindAllMethod()//
136                 .map(it -> Arrays.asList(it.getParameterTypes()).contains(Pageable.class))//
137                 .orElse(false);
138     }
139
140     /*
141      * (non-Javadoc)
142      * @see org.springframework.data.repository.core.RepositoryMetadata#getAlternativeDomainTypes()
143      */

144     @Override
145     public Set<Class<?>> getAlternativeDomainTypes() {
146         return Collections.emptySet();
147     }
148
149     /*
150      * (non-Javadoc)
151      * @see org.springframework.data.repository.core.RepositoryMetadata#isReactiveRepository()
152      */

153     @Override
154     public boolean isReactiveRepository() {
155         return ReactiveWrappers.usesReactiveType(repositoryInterface);
156     }
157 }
158