1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package io.wcm.handler.url.suffix;
21
22 import static io.wcm.handler.url.suffix.impl.UrlSuffixUtil.KEY_VALUE_DELIMITER;
23 import static io.wcm.handler.url.suffix.impl.UrlSuffixUtil.decodeKey;
24 import static io.wcm.handler.url.suffix.impl.UrlSuffixUtil.decodeResourcePathPart;
25 import static io.wcm.handler.url.suffix.impl.UrlSuffixUtil.decodeValue;
26 import static io.wcm.handler.url.suffix.impl.UrlSuffixUtil.splitSuffix;
27
28 import java.util.ArrayList;
29 import java.util.List;
30 import java.util.Objects;
31 import java.util.function.Predicate;
32 import java.util.stream.Collectors;
33
34 import org.apache.commons.lang3.StringUtils;
35 import org.apache.commons.lang3.math.NumberUtils;
36 import org.apache.sling.api.SlingHttpServletRequest;
37 import org.apache.sling.api.resource.Resource;
38 import org.jetbrains.annotations.NotNull;
39 import org.jetbrains.annotations.Nullable;
40 import org.osgi.annotation.versioning.ProviderType;
41
42 import com.day.cq.wcm.api.Page;
43 import com.day.cq.wcm.api.PageManager;
44
45 import io.wcm.sling.commons.adapter.AdaptTo;
46
47
48
49
50 @ProviderType
51 public final class SuffixParser {
52
53 private final SlingHttpServletRequest request;
54
55
56
57
58
59
60 public SuffixParser(@NotNull SlingHttpServletRequest request) {
61 this.request = request;
62 }
63
64
65
66
67
68
69
70
71
72 @SuppressWarnings("unchecked")
73 public <T> @Nullable T get(@NotNull String key, @NotNull Class<T> clazz) {
74 if (clazz == String.class) {
75 return (T)getString(key, (String)null);
76 }
77 if (clazz == Boolean.class) {
78 return (T)(Boolean)getBoolean(key, false);
79 }
80 if (clazz == Integer.class) {
81 return (T)(Integer)getInt(key, 0);
82 }
83 if (clazz == Long.class) {
84 return (T)(Long)getLong(key, 0L);
85 }
86 throw new IllegalArgumentException("Unsupported type: " + clazz.getName());
87 }
88
89
90
91
92
93
94
95
96
97 @SuppressWarnings("unchecked")
98 public <T> @Nullable T get(@NotNull String key, @Nullable T defaultValue) {
99 if (defaultValue instanceof String || defaultValue == null) {
100 return (T)getString(key, (String)defaultValue);
101 }
102 if (defaultValue instanceof Boolean) {
103 return (T)(Boolean)getBoolean(key, (Boolean)defaultValue);
104 }
105 if (defaultValue instanceof Integer) {
106 return (T)(Integer)getInt(key, (Integer)defaultValue);
107 }
108 if (defaultValue instanceof Long) {
109 return (T)(Long)getLong(key, (Long)defaultValue);
110 }
111 throw new IllegalArgumentException("Unsupported type: " + defaultValue.getClass().getName());
112 }
113
114 private String getString(String key, String defaultValue) {
115 String value = findSuffixPartByKey(key);
116 if (value == null) {
117 return defaultValue;
118 }
119 return value;
120 }
121
122 private boolean getBoolean(String key, boolean defaultValue) {
123 String value = findSuffixPartByKey(key);
124 if (value == null) {
125 return defaultValue;
126 }
127
128
129 if ("true".equals(value)) {
130 return true;
131 }
132 if ("false".equals(value)) {
133 return false;
134 }
135
136
137 return defaultValue;
138 }
139
140 private int getInt(String key, int defaultValue) {
141 String value = findSuffixPartByKey(key);
142 if (value == null) {
143 return defaultValue;
144 }
145 return NumberUtils.toInt(value, defaultValue);
146 }
147
148 private long getLong(String key, long defaultValue) {
149 String value = findSuffixPartByKey(key);
150 if (value == null) {
151 return defaultValue;
152 }
153 return NumberUtils.toLong(value, defaultValue);
154 }
155
156
157
158
159
160
161 private String findSuffixPartByKey(String key) {
162 for (String part : splitSuffix(request.getRequestPathInfo().getSuffix())) {
163 if (part.indexOf(KEY_VALUE_DELIMITER) >= 0) {
164 String partKey = decodeKey(part);
165 if (partKey.equals(key)) {
166 return decodeValue(part);
167 }
168 }
169 }
170 return null;
171 }
172
173
174
175
176
177
178 public @Nullable Resource getResource() {
179 return getResource((Predicate<Resource>)null, (Resource)null);
180 }
181
182
183
184
185
186
187 public @Nullable Resource getResource(@NotNull Resource baseResource) {
188 return getResource((Predicate<Resource>)null, baseResource);
189 }
190
191
192
193
194
195
196
197 public @Nullable Resource getResource(@NotNull Predicate<Resource> filter) {
198 return getResource(filter, (Resource)null);
199 }
200
201
202
203
204
205
206
207 public @Nullable Resource getResource(@Nullable Predicate<Resource> filter, @Nullable Resource baseResource) {
208 List<Resource> suffixResources = getResources(filter, baseResource);
209 if (suffixResources.isEmpty()) {
210 return null;
211 }
212 else {
213 return suffixResources.get(0);
214 }
215 }
216
217
218
219
220
221 public @NotNull List<Resource> getResources() {
222 return getResources((Predicate<Resource>)null, (Resource)null);
223 }
224
225
226
227
228
229
230 public @NotNull List<Resource> getResources(@NotNull Resource baseResource) {
231 return getResources((Predicate<Resource>)null, baseResource);
232 }
233
234
235
236
237
238
239 public @NotNull List<Resource> getResources(@NotNull Predicate<Resource> filter) {
240 return getResources(filter, (Resource)null);
241 }
242
243
244
245
246
247
248
249 @SuppressWarnings("java:S112")
250 public @NotNull List<Resource> getResources(@Nullable Predicate<Resource> filter, @Nullable Resource baseResource) {
251
252
253 Resource baseResourceToUse = baseResource;
254 if (baseResourceToUse == null) {
255 PageManager pageManager = request.getResourceResolver().adaptTo(PageManager.class);
256 if (pageManager == null) {
257 throw new RuntimeException("No page manager.");
258 }
259 Page currentPage = pageManager.getContainingPage(request.getResource());
260 if (currentPage != null) {
261 baseResourceToUse = currentPage.getContentResource();
262 }
263 else {
264 baseResourceToUse = request.getResource();
265 }
266 }
267 return getResourcesWithBaseResource(filter, baseResourceToUse);
268 }
269
270
271
272
273
274
275 public @Nullable Page getPage() {
276 return getPage((Predicate<Page>)null, (Page)null);
277 }
278
279
280
281
282
283
284 public @Nullable Page getPage(@NotNull Page basePage) {
285 return getPage((Predicate<Page>)null, basePage);
286 }
287
288
289
290
291
292
293
294
295 public @Nullable Page getPage(@NotNull Predicate<Page> filter) {
296 return getPage(filter, (Page)null);
297 }
298
299
300
301
302
303
304
305 public @Nullable Page getPage(@Nullable Predicate<Page> filter, @Nullable Page basePage) {
306 List<Page> suffixPages = getPages(filter, basePage);
307 if (suffixPages.isEmpty()) {
308 return null;
309 }
310 else {
311 return suffixPages.get(0);
312 }
313 }
314
315
316
317
318
319
320 public @NotNull List<Page> getPages() {
321 return getPages((Predicate<Page>)null, (Page)null);
322 }
323
324
325
326
327
328
329
330 public @NotNull List<Page> getPages(@NotNull Predicate<Page> filter) {
331 return getPages(filter, (Page)null);
332 }
333
334
335
336
337
338
339
340 @SuppressWarnings("null")
341 public @NotNull List<Page> getPages(@Nullable final Predicate<Page> filter, @Nullable final Page basePage) {
342 Resource baseResourceToUse = null;
343
344
345 if (basePage == null) {
346 PageManager pageManager = AdaptTo.notNull(request.getResourceResolver(), PageManager.class);
347 Page currentPage = pageManager.getContainingPage(request.getResource());
348 if (currentPage != null) {
349 baseResourceToUse = currentPage.adaptTo(Resource.class);
350 }
351 }
352 else {
353 baseResourceToUse = basePage.adaptTo(Resource.class);
354 }
355
356
357 Predicate<Resource> resourceFilter = resource -> {
358 Page page = resource.adaptTo(Page.class);
359 if (page == null) {
360 return false;
361 }
362 if (filter == null) {
363 return true;
364 }
365 return filter.test(page);
366 };
367 List<Resource> resources = getResourcesWithBaseResource(resourceFilter, baseResourceToUse);
368
369
370 return resources.stream()
371 .filter(Objects::nonNull)
372 .map(resource -> resource.adaptTo(Page.class))
373 .filter(Objects::nonNull)
374 .collect(Collectors.toList());
375 }
376
377 @SuppressWarnings("java:S135")
378 private @NotNull List<Resource> getResourcesWithBaseResource(@Nullable Predicate<Resource> filter, @Nullable Resource baseResource) {
379
380 String[] suffixParts = splitSuffix(request.getRequestPathInfo().getSuffix());
381
382
383 List<Resource> selectedResources = new ArrayList<>();
384 for (String path : suffixParts) {
385
386
387 if (StringUtils.contains(path, KEY_VALUE_DELIMITER)) {
388 continue;
389 }
390
391 String decodedPath = decodeResourcePathPart(path);
392
393
394 Resource resource = request.getResourceResolver().getResource(baseResource, decodedPath);
395 if (resource == null) {
396
397 continue;
398 }
399
400
401 if (filter == null || filter.test(resource)) {
402 selectedResources.add(resource);
403 }
404
405 }
406
407 return selectedResources;
408 }
409
410 }