1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.bremersee.apiclient.webflux;
18
19 import static java.util.Objects.nonNull;
20 import static org.springframework.core.annotation.AnnotationUtils.findAnnotation;
21 import static org.springframework.util.ObjectUtils.isArray;
22 import static org.springframework.util.ObjectUtils.isEmpty;
23 import static org.springframework.util.ObjectUtils.toObjectArray;
24
25 import java.lang.annotation.Annotation;
26 import java.lang.reflect.Parameter;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Collection;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Objects;
33 import java.util.Optional;
34 import java.util.Set;
35 import java.util.function.Function;
36 import java.util.stream.Collectors;
37 import lombok.EqualsAndHashCode;
38 import lombok.Getter;
39 import org.springframework.core.DefaultParameterNameDiscoverer;
40 import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
41 import org.springframework.util.Assert;
42 import org.springframework.util.LinkedMultiValueMap;
43 import org.springframework.util.MultiValueMap;
44
45
46
47
48
49
50 @SuppressWarnings("SameNameButDifferent")
51 @Getter
52 @EqualsAndHashCode(callSuper = true)
53 public class InvocationParameter extends Invocation {
54
55 private final Parameter parameter;
56
57 private final Object value;
58
59 private final int index;
60
61
62
63
64
65
66
67
68
69 public InvocationParameter(Invocation invocation, Parameter parameter, Object value, int index) {
70 super(invocation.getTargetClass(), invocation.getMethod(), invocation.getArgs());
71 Assert.notNull(parameter, "Parameter must be present.");
72 Assert.isTrue(
73 index >= 0 && index < invocation.getMethod().getParameters().length,
74 String.format("Illegal index [%s].", index));
75 this.parameter = parameter;
76 this.value = value;
77 this.index = index;
78 }
79
80
81
82
83
84
85 public String getParameterName() {
86 try {
87 String[] names = new DefaultParameterNameDiscoverer().getParameterNames(getMethod());
88 if (nonNull(names) && index >= 0 && index < names.length && !isEmpty(names[index])) {
89 return names[index];
90 }
91 } catch (Exception ignored) {
92
93 }
94 try {
95 String[] names = new LocalVariableTableParameterNameDiscoverer()
96 .getParameterNames(getMethod());
97 if (nonNull(names) && index >= 0 && index < names.length && !isEmpty(names[index])) {
98 return names[index];
99 }
100 } catch (Exception ignored) {
101
102 }
103 String name = parameter.getName();
104 return isEmpty(name) ? "arg" + index : name;
105 }
106
107
108
109
110
111
112
113 public boolean hasNoneParameterAnnotation(Set<Class<? extends Annotation>> annotationTypes) {
114 if (isEmpty(annotationTypes)) {
115 return true;
116 }
117 return annotationTypes.stream().noneMatch(this::hasParameterAnnotation);
118 }
119
120
121
122
123
124
125
126 public boolean hasParameterAnnotation(Class<? extends Annotation> annotationType) {
127 return findParameterAnnotation(annotationType).isPresent();
128 }
129
130
131
132
133
134
135
136
137 public <A extends Annotation> Optional<A> findParameterAnnotation(Class<A> annotationType) {
138 return Optional.ofNullable(findAnnotation(parameter, annotationType));
139 }
140
141 private <A extends Annotation> String getKey(
142 A annotation,
143 Function<A, String> keyExtractor) {
144
145 return Optional.ofNullable(annotation)
146 .map(keyExtractor)
147 .filter(name -> !name.isBlank())
148 .orElseGet(this::getParameterName);
149 }
150
151
152
153
154
155
156
157
158
159
160
161 public <E, A extends Annotation> MultiValueMap<String, E> toMultiValueMap(
162 Class<A> annotationType,
163 Function<A, String> keyExtractor,
164 Function<Object, E> valueMapper) {
165
166 return findParameterAnnotation(annotationType)
167 .map(annotation -> {
168 MultiValueMap<String, E> map = new LinkedMultiValueMap<>();
169 Object value = getValue();
170 if (value instanceof Map<?, ?>) {
171 map.putAll(toMultiValueMap((Map<?, ?>) value, valueMapper));
172 } else {
173 String key = getKey(
174 annotation,
175 keyExtractor);
176 map.put(key, toList(value, valueMapper));
177 }
178 return map;
179 })
180 .orElseGet(LinkedMultiValueMap::new);
181 }
182
183 private <E> MultiValueMap<String, E> toMultiValueMap(
184 Map<?, ?> map, Function<Object, E> valueMapper) {
185 MultiValueMap<String, E> multiValueMap = new LinkedMultiValueMap<>();
186 if (!isEmpty(map)) {
187 for (Map.Entry<?, ?> entry : map.entrySet()) {
188 String key = String.valueOf(entry.getKey());
189 List<E> value = toList(entry.getValue(), valueMapper);
190 if (!isEmpty(value)) {
191 multiValueMap.addAll(key, value);
192 }
193 }
194 }
195 return multiValueMap;
196 }
197
198 private <E> List<E> toList(Object value, Function<Object, E> valueMapper) {
199 List<E> list = new ArrayList<>();
200 if (isEmpty(value)) {
201 return list;
202 }
203 if (isArray(value)) {
204 return Arrays.stream(toObjectArray(value))
205 .filter(Objects::nonNull)
206 .map(valueMapper)
207 .collect(Collectors.toList());
208 }
209 if (value instanceof Collection<?>) {
210 return ((Collection<?>) value).stream()
211 .filter(Objects::nonNull)
212 .map(valueMapper)
213 .collect(Collectors.toList());
214 }
215 list.add(valueMapper.apply(value));
216 return list;
217 }
218
219 @Override
220 public String toString() {
221 return "InvocationParameter{"
222 + "targetClass=" + getTargetClass().getName()
223 + ", method=" + getMethod().getName()
224 + ", parameter=" + getParameterName()
225 + ", value=" + value
226 + ", index=" + index
227 + '}';
228 }
229 }