|
41 | 41 | * @author Christoph Strobl
|
42 | 42 | * @author Mark Paluch
|
43 | 43 | * @author Mariusz Mączkowski
|
| 44 | + * @author Jens Schauder |
44 | 45 | */
|
45 | 46 | public class PropertyPath implements Streamable<PropertyPath> {
|
46 | 47 |
|
@@ -108,12 +109,53 @@ public TypeInformation<?> getOwningType() {
|
108 | 109 | }
|
109 | 110 |
|
110 | 111 | /**
|
111 |
| - * Returns the name of the {@link PropertyPath}. |
| 112 | + * Extracts the {@link PropertyPath} chain from the given source {@link String} and {@link TypeInformation}. <br /> |
| 113 | + * Uses {@link #SPLITTER} by default and {@link #SPLITTER_FOR_QUOTED} for {@link Pattern#quote(String) quoted} |
| 114 | + * literals. Separate parts of the path may be separated by {@code "."} or by {@code "_"} or by camel case. When the |
| 115 | + * match to properties is ambiguous longer property names are preferred. So for "userAddressCity" the interpretation |
| 116 | + * "userAddress.city" is preferred over "user.address.city". |
112 | 117 | *
|
113 |
| - * @return the name will never be {@literal null}. |
| 118 | + * @param source must not be {@literal null}. |
| 119 | + * @param type |
| 120 | + * @return |
114 | 121 | */
|
115 |
| - public String getSegment() { |
116 |
| - return name; |
| 122 | + public static PropertyPath from(String source, TypeInformation<?> type) { |
| 123 | + |
| 124 | + Assert.hasText(source, "Source must not be null or empty!"); |
| 125 | + Assert.notNull(type, "TypeInformation must not be null or empty!"); |
| 126 | + |
| 127 | + return cache.computeIfAbsent(Key.of(type, source), it -> { |
| 128 | + |
| 129 | + List<String> iteratorSource = new ArrayList<>(); |
| 130 | + |
| 131 | + Matcher matcher = isQuoted(it.path) ? SPLITTER_FOR_QUOTED.matcher(it.path.replace("\\Q", "").replace("\\E", "")) |
| 132 | + : SPLITTER.matcher("_" + it.path); |
| 133 | + |
| 134 | + while (matcher.find()) { |
| 135 | + iteratorSource.add(matcher.group(1)); |
| 136 | + } |
| 137 | + |
| 138 | + Iterator<String> parts = iteratorSource.iterator(); |
| 139 | + |
| 140 | + PropertyPath result = null; |
| 141 | + Stack<PropertyPath> current = new Stack<>(); |
| 142 | + |
| 143 | + while (parts.hasNext()) { |
| 144 | + if (result == null) { |
| 145 | + result = create(parts.next(), it.type, current); |
| 146 | + current.push(result); |
| 147 | + } else { |
| 148 | + current.push(create(parts.next(), current)); |
| 149 | + } |
| 150 | + } |
| 151 | + |
| 152 | + if (result == null) { |
| 153 | + throw new IllegalStateException( |
| 154 | + String.format("Expected parsing to yield a PropertyPath from %s but got null!", source)); |
| 155 | + } |
| 156 | + |
| 157 | + return result; |
| 158 | + }); |
117 | 159 | }
|
118 | 160 |
|
119 | 161 | /**
|
@@ -156,14 +198,20 @@ public TypeInformation<?> getTypeInformation() {
|
156 | 198 | }
|
157 | 199 |
|
158 | 200 | /**
|
159 |
| - * Returns the next nested {@link PropertyPath}. |
| 201 | + * Returns the first part of the {@link PropertyPath}. |
160 | 202 | *
|
161 |
| - * @return the next nested {@link PropertyPath} or {@literal null} if no nested {@link PropertyPath} available. |
162 |
| - * @see #hasNext() |
| 203 | + * <pre> |
| 204 | + * {@code |
| 205 | + * PropertyPath.from("a.b.c", Some.class).getSegment(); |
| 206 | + * } |
| 207 | + * </pre> |
| 208 | + * |
| 209 | + * will result in {@code "a"} |
| 210 | + * |
| 211 | + * @return the name will never be {@literal null}. |
163 | 212 | */
|
164 |
| - @Nullable |
165 |
| - public PropertyPath next() { |
166 |
| - return next; |
| 213 | + public String getSegment() { |
| 214 | + return name; |
167 | 215 | }
|
168 | 216 |
|
169 | 217 | /**
|
@@ -214,37 +262,23 @@ public PropertyPath nested(String path) {
|
214 | 262 | return PropertyPath.from(lookup, owningType);
|
215 | 263 | }
|
216 | 264 |
|
217 |
| - /* |
218 |
| - * (non-Javadoc) |
219 |
| - * @see java.lang.Iterable#iterator() |
| 265 | + /** |
| 266 | + * Returns the {@link PropertyPath} path that results from removing the first element of the current one. |
| 267 | + * |
| 268 | + * <pre> |
| 269 | + * {@code |
| 270 | + * System.out.println(PropertyPath.from("a.b.c", Some.class).next().toDotPath()); |
| 271 | + * } |
| 272 | + * </pre> |
| 273 | + * |
| 274 | + * Will result in the output: {@code b.c} |
| 275 | + * |
| 276 | + * @return the next nested {@link PropertyPath} or {@literal null} if no nested {@link PropertyPath} available. |
| 277 | + * @see #hasNext() |
220 | 278 | */
|
221 |
| - public Iterator<PropertyPath> iterator() { |
222 |
| - |
223 |
| - return new Iterator<PropertyPath>() { |
224 |
| - |
225 |
| - private @Nullable PropertyPath current = PropertyPath.this; |
226 |
| - |
227 |
| - public boolean hasNext() { |
228 |
| - return current != null; |
229 |
| - } |
230 |
| - |
231 |
| - @Nullable |
232 |
| - public PropertyPath next() { |
233 |
| - |
234 |
| - PropertyPath result = current; |
235 |
| - |
236 |
| - if (result == null) { |
237 |
| - return null; |
238 |
| - } |
239 |
| - |
240 |
| - this.current = result.next(); |
241 |
| - return result; |
242 |
| - } |
243 |
| - |
244 |
| - public void remove() { |
245 |
| - throw new UnsupportedOperationException(); |
246 |
| - } |
247 |
| - }; |
| 279 | + @Nullable |
| 280 | + public PropertyPath next() { |
| 281 | + return next; |
248 | 282 | }
|
249 | 283 |
|
250 | 284 | /*
|
@@ -332,51 +366,55 @@ public static PropertyPath from(String source, Class<?> type) {
|
332 | 366 | }
|
333 | 367 |
|
334 | 368 | /**
|
335 |
| - * Extracts the {@link PropertyPath} chain from the given source {@link String} and {@link TypeInformation}. <br /> |
336 |
| - * Uses {@link #SPLITTER} by default and {@link #SPLITTER_FOR_QUOTED} for {@link Pattern#quote(String) quoted} |
337 |
| - * literals. |
| 369 | + * Returns an {@link Iterator<PropertyPath>} that iterates over all the partial property paths with the same leaf type |
| 370 | + * but decreasing length. |
338 | 371 | *
|
339 |
| - * @param source must not be {@literal null}. |
340 |
| - * @param type |
341 |
| - * @return |
| 372 | + * <pre> |
| 373 | + * {@code |
| 374 | + * PropertyPath propertyPath = PropertyPath.from("a.b.c", Some.class); |
| 375 | + * for (p : propertyPath.iterator()) { |
| 376 | + * System.out.println(p.toDotPath()); |
| 377 | + * }; |
| 378 | + * } |
| 379 | + * </pre> |
| 380 | + * |
| 381 | + * Results in the output: |
| 382 | + * |
| 383 | + * <pre> |
| 384 | + * {@code |
| 385 | + * a.b.c |
| 386 | + * b.c |
| 387 | + * c |
| 388 | + * } |
| 389 | + * </pre> |
342 | 390 | */
|
343 |
| - public static PropertyPath from(String source, TypeInformation<?> type) { |
344 |
| - |
345 |
| - Assert.hasText(source, "Source must not be null or empty!"); |
346 |
| - Assert.notNull(type, "TypeInformation must not be null or empty!"); |
347 |
| - |
348 |
| - return cache.computeIfAbsent(Key.of(type, source), it -> { |
| 391 | + public Iterator<PropertyPath> iterator() { |
349 | 392 |
|
350 |
| - List<String> iteratorSource = new ArrayList<>(); |
| 393 | + return new Iterator<PropertyPath>() { |
351 | 394 |
|
352 |
| - Matcher matcher = isQuoted(it.path) ? SPLITTER_FOR_QUOTED.matcher(it.path.replace("\\Q", "").replace("\\E", "")) |
353 |
| - : SPLITTER.matcher("_" + it.path); |
| 395 | + private @Nullable PropertyPath current = PropertyPath.this; |
354 | 396 |
|
355 |
| - while (matcher.find()) { |
356 |
| - iteratorSource.add(matcher.group(1)); |
| 397 | + public boolean hasNext() { |
| 398 | + return current != null; |
357 | 399 | }
|
358 | 400 |
|
359 |
| - Iterator<String> parts = iteratorSource.iterator(); |
| 401 | + @Nullable |
| 402 | + public PropertyPath next() { |
360 | 403 |
|
361 |
| - PropertyPath result = null; |
362 |
| - Stack<PropertyPath> current = new Stack<>(); |
| 404 | + PropertyPath result = current; |
363 | 405 |
|
364 |
| - while (parts.hasNext()) { |
365 | 406 | if (result == null) {
|
366 |
| - result = create(parts.next(), it.type, current); |
367 |
| - current.push(result); |
368 |
| - } else { |
369 |
| - current.push(create(parts.next(), current)); |
| 407 | + return null; |
370 | 408 | }
|
371 |
| - } |
372 | 409 |
|
373 |
| - if (result == null) { |
374 |
| - throw new IllegalStateException( |
375 |
| - String.format("Expected parsing to yield a PropertyPath from %s but got null!", source)); |
| 410 | + this.current = result.next(); |
| 411 | + return result; |
376 | 412 | }
|
377 | 413 |
|
378 |
| - return result; |
379 |
| - }); |
| 414 | + public void remove() { |
| 415 | + throw new UnsupportedOperationException(); |
| 416 | + } |
| 417 | + }; |
380 | 418 | }
|
381 | 419 |
|
382 | 420 | private static boolean isQuoted(String source) {
|
|
0 commit comments