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