1 /*
2  * Copyright 2008-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.query;
17
18 import java.util.Iterator;
19 import java.util.Optional;
20
21 import org.springframework.data.domain.Pageable;
22 import org.springframework.data.domain.Sort;
23 import org.springframework.data.repository.util.QueryExecutionConverters;
24 import org.springframework.lang.Nullable;
25 import org.springframework.util.Assert;
26
27 /**
28  * {@link ParameterAccessor} implementation using a {@link Parameters} instance to find special parameters.
29  *
30  * @author Oliver Gierke
31  * @author Mark Paluch
32  */

33 public class ParametersParameterAccessor implements ParameterAccessor {
34
35     private final Parameters<?, ?> parameters;
36     private final Object[] values;
37
38     /**
39      * Creates a new {@link ParametersParameterAccessor}.
40      *
41      * @param parameters must not be {@literal null}.
42      * @param values must not be {@literal null}.
43      */

44     public ParametersParameterAccessor(Parameters<?, ?> parameters, Object[] values) {
45
46         Assert.notNull(parameters, "Parameters must not be null!");
47         Assert.notNull(values, "Values must not be null!");
48
49         Assert.isTrue(parameters.getNumberOfParameters() == values.length, "Invalid number of parameters given!");
50
51         this.parameters = parameters;
52
53         if (requiresUnwrapping(values)) {
54             this.values = new Object[values.length];
55
56             for (int i = 0; i < values.length; i++) {
57                 this.values[i] = QueryExecutionConverters.unwrap(values[i]);
58             }
59         } else {
60             this.values = values;
61         }
62     }
63
64     private static boolean requiresUnwrapping(Object[] values) {
65
66         for (Object value : values) {
67             if (value != null && QueryExecutionConverters.supports(value.getClass())) {
68                 return true;
69             }
70         }
71
72         return false;
73     }
74
75     /**
76      * Returns the {@link Parameters} instance backing the accessor.
77      *
78      * @return the parameters will never be {@literal null}.
79      */

80     public Parameters<?, ?> getParameters() {
81         return parameters;
82     }
83
84     /**
85      * Returns the potentially unwrapped values.
86      *
87      * @return
88      */

89     protected Object[] getValues() {
90         return this.values;
91     }
92
93     /*
94      * (non-Javadoc)
95      * @see org.springframework.data.repository.query.ParameterAccessor#getPageable()
96      */

97     public Pageable getPageable() {
98
99         if (!parameters.hasPageableParameter()) {
100             return Pageable.unpaged();
101         }
102
103         Pageable pageable = (Pageable) values[parameters.getPageableIndex()];
104
105         return pageable == null ? Pageable.unpaged() : pageable;
106     }
107
108     /*
109      * (non-Javadoc)
110      * @see org.springframework.data.repository.query.ParameterAccessor#getSort()
111      */

112     public Sort getSort() {
113
114         if (parameters.hasSortParameter()) {
115
116             Sort sort = (Sort) values[parameters.getSortIndex()];
117             return sort == null ? Sort.unsorted() : sort;
118         }
119
120         if (parameters.hasPageableParameter()) {
121             return getPageable().getSort();
122         }
123
124         return Sort.unsorted();
125     }
126
127     /**
128      * Returns the dynamic projection type if available, {@literal null} otherwise.
129      *
130      * @return
131      */

132     public Optional<Class<?>> getDynamicProjection() {
133
134         return Optional.ofNullable(parameters.hasDynamicProjection() //
135                 ? (Class<?>) values[parameters.getDynamicProjectionIndex()] //
136                 : null);
137     }
138
139     /**
140      * Returns the dynamic projection type if available, {@literal null} otherwise.
141      *
142      * @return
143      */

144     @Nullable
145     public Class<?> findDynamicProjection() {
146
147         return parameters.hasDynamicProjection() //
148                 ? (Class<?>) values[parameters.getDynamicProjectionIndex()]
149                 : null;
150     }
151
152     /**
153      * Returns the value with the given index.
154      *
155      * @param index
156      * @return
157      */

158     @SuppressWarnings("unchecked")
159     protected <T> T getValue(int index) {
160         return (T) values[index];
161     }
162
163     /*
164      * (non-Javadoc)
165      * @see org.springframework.data.repository.query.ParameterAccessor#getBindableValue(int)
166      */

167     public Object getBindableValue(int index) {
168         return values[parameters.getBindableParameter(index).getIndex()];
169     }
170
171     /*
172      * (non-Javadoc)
173      * @see org.springframework.data.repository.query.ParameterAccessor#hasBindableNullValue()
174      */

175     public boolean hasBindableNullValue() {
176
177         for (Parameter parameter : parameters.getBindableParameters()) {
178             if (values[parameter.getIndex()] == null) {
179                 return true;
180             }
181         }
182
183         return false;
184     }
185
186     /*
187      * (non-Javadoc)
188      * @see org.springframework.data.repository.query.ParameterAccessor#iterator()
189      */

190     public BindableParameterIterator iterator() {
191         return new BindableParameterIterator(this);
192     }
193
194     /**
195      * Iterator class to allow traversing all bindable parameters inside the accessor.
196      *
197      * @author Oliver Gierke
198      */

199     private static class BindableParameterIterator implements Iterator<Object> {
200
201         private final int bindableParameterCount;
202         private final ParameterAccessor accessor;
203
204         private int currentIndex = 0;
205
206         /**
207          * Creates a new {@link BindableParameterIterator}.
208          *
209          * @param accessor must not be {@literal null}.
210          */

211         public BindableParameterIterator(ParametersParameterAccessor accessor) {
212
213             Assert.notNull(accessor, "ParametersParameterAccessor must not be null!");
214
215             this.accessor = accessor;
216             this.bindableParameterCount = accessor.getParameters().getBindableParameters().getNumberOfParameters();
217         }
218
219         /**
220          * Returns the next bindable parameter.
221          *
222          * @return
223          */

224         public Object next() {
225             return accessor.getBindableValue(currentIndex++);
226         }
227
228         /*
229          * (non-Javadoc)
230          * @see java.util.Iterator#hasNext()
231          */

232         public boolean hasNext() {
233             return bindableParameterCount > currentIndex;
234         }
235
236         /*
237          * (non-Javadoc)
238          * @see java.util.Iterator#remove()
239          */

240         public void remove() {
241             throw new UnsupportedOperationException();
242         }
243     }
244 }
245