Skip to content

Package: Finder

Finder

nameinstructionbranchcomplexitylinemethod
empty()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
firstResult()
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
forEach(Consumer)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
from(Optional)
M: 8 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
from(OptionalInt)
M: 10 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
iterator()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
mapping(Finder, Function)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
max(Optional)
M: 8 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
max(OptionalInt)
M: 10 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
ofCloned(Collection)
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
ofProvider(BiFunction)
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
ofSupplier(Supplier)
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
ofType(Class)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
optionalFirstResult()
M: 0 C: 4
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
optionalResult()
M: 18 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
result()
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
sort(Finder.SortCriterion)
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
stream()
M: 0 C: 4
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
withContext(Object)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /*
2: * *************************************************************************************************************************************************************
3: *
4: * TheseFoolishThings: Miscellaneous utilities
5: * http://tidalwave.it/projects/thesefoolishthings
6: *
7: * Copyright (C) 2009 - 2025 by Tidalwave s.a.s. (http://tidalwave.it)
8: *
9: * *************************************************************************************************************************************************************
10: *
11: * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
12: * You may obtain a copy of the License at
13: *
14: * http://www.apache.org/licenses/LICENSE-2.0
15: *
16: * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
17: * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
18: *
19: * *************************************************************************************************************************************************************
20: *
21: * git clone https://bitbucket.org/tidalwave/thesefoolishthings-src
22: * git clone https://github.com/tidalwave-it/thesefoolishthings-src
23: *
24: * *************************************************************************************************************************************************************
25: */
26: package it.tidalwave.util;
27:
28: // import javax.annotation.Nonnegative;
29: import jakarta.annotation.Nonnull;
30: import java.util.Collection;
31: import java.util.Collections;
32: import java.util.Comparator;
33: import java.util.Iterator;
34: import java.util.List;
35: import java.util.Optional;
36: import java.util.OptionalInt;
37: import java.util.function.BiFunction;
38: import java.util.function.Consumer;
39: import java.util.function.Function;
40: import java.util.function.Supplier;
41: import java.util.stream.Stream;
42: import java.io.Serializable;
43: import it.tidalwave.util.impl.finder.ArrayListFinder;
44: import it.tidalwave.util.impl.finder.MappingFinder;
45: import it.tidalwave.util.impl.finder.ProviderFinder;
46: import it.tidalwave.util.impl.finder.SupplierFinder;
47: import lombok.AccessLevel;
48: import lombok.AllArgsConstructor;
49: import lombok.EqualsAndHashCode;
50: import lombok.RequiredArgsConstructor;
51: import lombok.ToString;
52:
53: /***************************************************************************************************************************************************************
54: *
55: * A factory for providing results of a search. {@code Finder} implementations must be <em>immutable</em>.
56: *
57: * @author Fabrizio Giudici
58: *
59: **************************************************************************************************************************************************************/
60: public interface Finder<T> extends Cloneable, Serializable
61: {
62: /***********************************************************************************************************************************************************
63: * A tag interface to mark objects which are meaningful sort criteria that can be passed to {@link Finder#sort(it.tidalwave.util.Finder.SortCriterion)}.
64: * In general, a {@code SortCriterion} is just a behaviourless and methodless object, that should be specifically handled by concrete implementations of
65: * {@link Finder}. The only exceptions are {@link InMemorySortCriterion} objects.
66: **********************************************************************************************************************************************************/
67: public static interface SortCriterion
68: {
69: public static final Class<SortCriterion> _SortCriterion_ = SortCriterion.class;
70:
71: /** A special {@link SortCriterion} which indicates that no sort has been performed. */
72: public static final SortCriterion UNSORTED = (InMemorySortCriterion<Object>)(results, sortDirection) -> {};
73:
74: public static final SortCriterion DEFAULT = UNSORTED;
75: }
76:
77: /***********************************************************************************************************************************************************
78: * An interface that should be implemented by specific {@link SortCriterion} objects which are capable to implement by themselves the sorting of objects, by
79: * post-processing an existing collection of objects. While this is often convenient, it is possible for it to be inefficient in cases in which the original
80: * source of objects is capable to perform the sort in an optimized way (e.g. an SQL database by means of {@code ORDER BY}). The facility class
81: * {@link it.tidalwave.util.spi.HierarchicFinderSupport} supports {@code FilterSortCriterion} objects out of the box.
82: **********************************************************************************************************************************************************/
83: public static interface InMemorySortCriterion<U> extends SortCriterion
84: {
85: /*******************************************************************************************************************************************************
86: * Performs the sort of results.
87: * @param results the list of objects to be sorted in place
88: ******************************************************************************************************************************************************/
89: public default void sort (@Nonnull final List<? extends U> results)
90: {
91: sort(results, SortDirection.ASCENDING);
92: }
93:
94: /*******************************************************************************************************************************************************
95: * Performs the sort of results.
96: * @param results the list of objects to be sorted in place
97: * @param sortDirection the sort direction
98: ******************************************************************************************************************************************************/
99: // START SNIPPET: sort
100: public void sort (@Nonnull List<? extends U> results, @Nonnull SortDirection sortDirection);
101: // END SNIPPET: sort
102:
103: /*******************************************************************************************************************************************************
104: * Creates a new in-memory {@code SortCriterion} based on a {@link Comparator}.
105: * @param <U> the type of the objects to compare
106: * @param comparator the {@code Comparator}
107: * @return the new {@code SortCriterion}
108: ******************************************************************************************************************************************************/
109: @Nonnull
110: public static <U> InMemorySortCriterion<U> of (@Nonnull final Comparator<? super U> comparator)
111: {
112: return of(comparator, comparator.getClass().getSimpleName());
113: }
114:
115: /*******************************************************************************************************************************************************
116: * Creates a new in-memory {@code SortCriterion} based on a {@link Comparator}.
117: * @param <U> the type of the objects to compare
118: * @param comparator the {@code Comparator}
119: * @param name a name
120: * @return the new {@code SortCriterion}
121: ******************************************************************************************************************************************************/
122: @Nonnull
123: public static <U> InMemorySortCriterion<U> of (@Nonnull final Comparator<? super U> comparator,
124: @Nonnull final String name)
125: {
126: return new DefaultInMemorySortCriterion<>(comparator, name);
127: }
128:
129: /*******************************************************************************************************************************************************
130: *
131: ******************************************************************************************************************************************************/
132: @AllArgsConstructor @ToString @EqualsAndHashCode
133: static class DefaultInMemorySortCriterion<U> implements Finder.InMemorySortCriterion<U>, Serializable
134: {
135: private static final long serialVersionUID = 76093596048395982L;
136:
137: @Nonnull
138: private final Comparator<? super U> comparator;
139:
140: @Nonnull
141: private final String name;
142:
143: @Override
144: public void sort (@Nonnull final List<? extends U> results, @Nonnull final SortDirection sortDirection)
145: {
146: results.sort((Comparator<U>)(o1, o2) -> comparator.compare(o1, o2) * sortDirection.intValue());
147: }
148: }
149: }
150:
151: /***********************************************************************************************************************************************************
152: * An enumeration to define the direction of a sort (ascending or descending).
153: **********************************************************************************************************************************************************/
154: @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
155: public static enum SortDirection
156: {
157: ASCENDING(+1), DESCENDING(-1);
158:
159: private final int intValue;
160:
161: /** @return +1 for ascending direction, -1 for descending */
162: public int intValue()
163: {
164: return intValue;
165: }
166: }
167:
168: /***********************************************************************************************************************************************************
169: * Tells the {@code Finder} that only a subset of found items will be returned, starting from the given position.
170: * @param firstResult the index of the first result to return
171: * @return the {@code Finder}
172: **********************************************************************************************************************************************************/
173: // START SNIPPET: from
174: @Nonnull
175: public Finder<T> from (/* @Nonnegative */ int firstResult);
176: // END SNIPPET: from
177:
178: /***********************************************************************************************************************************************************
179: * Tells the {@code Finder} that only a subset of found items will be returned, starting from the given position.
180: * @param firstResult the index of the first result to return
181: * @return the {@code Finder}
182: * @since 3.2-ALPHA-19
183: **********************************************************************************************************************************************************/
184: @Nonnull
185: public default Finder<T> from (@Nonnull final Optional<Integer> firstResult)
186: {
187: return firstResult.map(this::from).orElse(this);
188: }
189:
190: /***********************************************************************************************************************************************************
191: * Tells the {@code Finder} that only a subset of found items will be returned, starting from the given position.
192: * @param firstResult the index of the first result to return
193: * @return the {@code Finder}
194: * @since 3.2-ALPHA-22
195: **********************************************************************************************************************************************************/
196: @Nonnull
197: public default Finder<T> from (@Nonnull final OptionalInt firstResult)
198: {
199:• return firstResult.isPresent() ? from(firstResult.getAsInt()) : this;
200: }
201:
202: /***********************************************************************************************************************************************************
203: * Tells the {@code Finder} that only a maximum number of found items will be returned.
204: * @param maxResults the max number of results to return
205: * @return the {@code Finder}
206: **********************************************************************************************************************************************************/
207: // START SNIPPET: max
208: @Nonnull
209: public Finder<T> max (/* @Nonnegative */ int maxResults);
210: // END SNIPPET: max
211:
212: /***********************************************************************************************************************************************************
213: * Tells the {@code Finder} that only a maximum number of found items will be returned.
214: * @param maxResults the max number of results to return
215: * @return the {@code Finder}
216: * @since 3.2-ALPHA-19
217: **********************************************************************************************************************************************************/
218: @Nonnull
219: public default Finder<T> max (@Nonnull final Optional<Integer> maxResults)
220: {
221: return maxResults.map(this::max).orElse(this);
222: }
223:
224: /***********************************************************************************************************************************************************
225: * Tells the {@code Finder} that only a maximum number of found items will be returned.
226: * @param maxResults the max number of results to return
227: * @return the {@code Finder}
228: * @since 3.2-ALPHA-22
229: **********************************************************************************************************************************************************/
230: @Nonnull
231: public default Finder<T> max (@Nonnull final OptionalInt maxResults)
232: {
233:• return maxResults.isPresent() ? max(maxResults.getAsInt()) : this;
234: }
235:
236: /***********************************************************************************************************************************************************
237: * Tells the {@code Finder} that results should be created with the given context. This method can be called multiple times; contexts are accumulated.
238: * Not all implementations of {@code Finder} have this working.
239: * @param context the context
240: * @return the {@code Finder}
241: **********************************************************************************************************************************************************/
242: @Nonnull
243: public default Finder<T> withContext (@Nonnull final Object context)
244: {
245: throw new UnsupportedOperationException("Not implemented.");
246: }
247:
248: /***********************************************************************************************************************************************************
249: * Tells the {@code Finder} that the specified type of results is expected.
250: * Not all implementations of {@code Finder} have this working.
251: * @param <U> the static type
252: * @param type the dynamic type
253: * @return the {@code Finder}
254: **********************************************************************************************************************************************************/
255: @Nonnull
256: public default <U> Finder<U> ofType (@Nonnull final Class<U> type)
257: {
258: throw new UnsupportedOperationException("Not implemented.");
259: }
260:
261: /***********************************************************************************************************************************************************
262: * Tells the {@code Finder} that results will be sorted according to the given criterion, in ascending direction.
263: * @param criterion the sort criterion
264: * @return the {@code Finder}
265: **********************************************************************************************************************************************************/
266: @Nonnull
267: public default Finder<T> sort (@Nonnull final SortCriterion criterion)
268: {
269: return sort(criterion, SortDirection.ASCENDING);
270: }
271:
272: /***********************************************************************************************************************************************************
273: * Tells the {@code Finder} that results will be sorted according to the given criterion and direction.
274: * @param criterion the sort criterion
275: * @param direction the sort direction
276: * @return the {@code Finder}
277: **********************************************************************************************************************************************************/
278: @Nonnull
279: public Finder<T> sort (@Nonnull SortCriterion criterion, @Nonnull SortDirection direction);
280:
281: /***********************************************************************************************************************************************************
282: * Performs the search and returns the found items.
283: * @return the searched items
284: **********************************************************************************************************************************************************/
285: // START SNIPPET: results
286: @Nonnull
287: public List<T> results();
288: // END SNIPPET: results
289:
290: /***********************************************************************************************************************************************************
291: * Performs the search and returns the count of found items.
292: * @return the count of found items
293: **********************************************************************************************************************************************************/
294: // START SNIPPET: count
295: /* @Nonnegative */
296: public int count();
297: // END SNIPPET: count
298:
299: /***********************************************************************************************************************************************************
300: * Performs the search assuming that it will return a single item and returns it. This method fails if the search returns more than one single item.
301: * @return the optional result
302: * @throws RuntimeException if the search returned more than one single item
303: * @since 3.2-ALPHA-1 (previously in Finder8)
304: **********************************************************************************************************************************************************/
305: // START SNIPPET: optionalResult
306: @Nonnull
307: public default Optional<T> optionalResult()
308: // END SNIPPET: optionalResult
309: {
310: final var results = results();
311:
312:• if (results.size() > 1)
313: {
314: throw new RuntimeException(results.size() + " results, expected only one");
315: }
316:
317: return results.stream().findFirst();
318: }
319:
320: /***********************************************************************************************************************************************************
321: * Performs the search and returns only the first found item.
322: * @return the first result
323: * @since 3.2-ALPHA-1 (previously in Finder8)
324: **********************************************************************************************************************************************************/
325: // START SNIPPET: optionalFirstResult
326: @Nonnull
327: public default Optional<T> optionalFirstResult()
328: // END SNIPPET: optionalFirstResult
329: {
330: return stream().findFirst();
331: }
332:
333: /***********************************************************************************************************************************************************
334: * Returns a stream of results.
335: * @return the stream
336: * @since 3.2-ALPHA-1 (previously in Finder8)
337: **********************************************************************************************************************************************************/
338: @Nonnull
339: public default Stream<T> stream()
340: {
341: return results().stream();
342: }
343:
344: /***********************************************************************************************************************************************************
345: * Returns an iterator of results.
346: * @return the iterator
347: * @since 3.2-ALPHA-1 (previously in Finder8)
348: **********************************************************************************************************************************************************/
349: @Nonnull
350: public default Iterator<T> iterator()
351: {
352: return stream().iterator();
353: }
354:
355: /***********************************************************************************************************************************************************
356: * Iterates through results.
357: * @param consumer the consumer
358: * @since 3.2-ALPHA-22
359: **********************************************************************************************************************************************************/
360: public default void forEach (@Nonnull final Consumer<? super T> consumer)
361: {
362: stream().forEach(consumer);
363: }
364:
365: /***********************************************************************************************************************************************************
366: * Performs the search assuming that it will return a single item and returns it. This method fails if the search returns more than one single item.
367: * @return the found item
368: * @throws NotFoundException if the search didn't find anything
369: * @throws RuntimeException if the search returned more than one single item
370: * @deprecated Use {@link #optionalResult()} instead
371: **********************************************************************************************************************************************************/
372: @Nonnull @Deprecated
373: public default T result()
374: throws NotFoundException
375: {
376: return optionalResult().orElseThrow(NotFoundException::new);
377: }
378:
379: /***********************************************************************************************************************************************************
380: * Performs the search and returns only the first found item.
381: * @return the first found item
382: * @throws NotFoundException if the search didn't find anything
383: * @deprecated Use {@link #optionalFirstResult()} instead
384: **********************************************************************************************************************************************************/
385: @Nonnull @Deprecated
386: public default T firstResult()
387: throws NotFoundException
388: {
389: return optionalFirstResult().orElseThrow(NotFoundException::new);
390: }
391:
392: /***********************************************************************************************************************************************************
393: * Returns an empty {@code Finder}.
394: * @param <U> the type of the {@code Finder}
395: * @return the empty {@code Finder}
396: * @since 3.2-ALPHA-1 (previously in HierarchicFinderSupport.emptyFinder())
397: **********************************************************************************************************************************************************/
398: @Nonnull
399: public static <U> Finder<U> empty()
400: {
401: return ofCloned(Collections.emptyList());
402: }
403:
404: /***********************************************************************************************************************************************************
405: * Returns a wrapped {@code Finder} on a given collection of elements. The collection is cloned and will be immutable. If you need to compute the collection
406: * on demand, use {@link #ofSupplier(Supplier)}. This method retrieves the full range of results that will be later segmented in compliance with the values
407: * specified by {@link #from(int)} and {@link #max(int)}; this is ok if the whole list of results is already available of if it is not expensive to compute.
408: * The alternate method {@link #ofProvider(BiFunction)} allows to access the 'from' and 'max' parameter, so only the required items need to be provided.
409: * @param <U> the type of the {@code Finder}
410: * @param items the objects to wrap
411: * @return the wrapped {@code Finder}
412: * @see #ofSupplier(Supplier)
413: * @see #ofProvider(BiFunction)
414: * @since 3.2-ALPHA-1
415: **********************************************************************************************************************************************************/
416: // START SNIPPET: ofCloned
417: @Nonnull
418: public static <U> Finder<U> ofCloned (@Nonnull final Collection<? extends U> items)
419: // END SNIPPET: ofCloned
420: {
421: return new ArrayListFinder<>(items);
422: }
423:
424: /***********************************************************************************************************************************************************
425: * Returns a wrapped {@code Finder} on a given supplier. The collection will be cloned after being supplied. This method retrieves the full range of results
426: * that will be later segmented in compliance with the values specified by {@link #from(int)} and {@link #max(int)}; this is ok if the whole list of results
427: * is already available of if it is not expensive to compute. The alternate method {@link #ofProvider(BiFunction)} allows to access the 'from' and 'max'
428: * parameter, so only the required items need to be provided.
429: * @param <U> the type of the {@code Finder}
430: * @param supplier the supplier
431: * @return the wrapped {@code Finder}
432: * @see #ofCloned(Collection)
433: * @see #ofProvider(BiFunction)
434: * @since 3.2-ALPHA-15
435: **********************************************************************************************************************************************************/
436: // START SNIPPET: ofsupplier
437: @Nonnull
438: public static <U> Finder<U> ofSupplier (@Nonnull final Supplier<? extends Collection<? extends U>> supplier)
439: // END SNIPPET: ofsupplier
440: {
441: return new SupplierFinder<>(supplier);
442: }
443:
444: /***********************************************************************************************************************************************************
445: * Returns a wrapped {@code Finder} on a given function to provide results. The function receives the 'from' and 'max' arguments to select a subrange of the
446: * results. The collection will be cloned after being supplied.
447: * @param <U> the type of the {@code Finder}
448: * @param provider the function providing results
449: * @return the wrapped {@code Finder}
450: * @see #ofCloned(Collection)
451: * @see #ofSupplier(Supplier)
452: * @since 3.2-ALPHA-15
453: **********************************************************************************************************************************************************/
454: // START SNIPPET: ofProvider
455: @Nonnull
456: public static <U> Finder<U> ofProvider (@Nonnull final BiFunction<Integer, Integer, ? extends Collection<? extends U>> provider)
457: // END SNIPPET: ofProvider
458: {
459: return new ProviderFinder<>(provider);
460: }
461:
462: /***********************************************************************************************************************************************************
463: * Returns a mapping {@code Finder} on a given delegate {@code Finder}. The mapper finder provides the same results as the delegate, transformed by a mapper
464: * function.
465: * @param <U> the type of the {@code Finder}
466: * @param <V> the type of the delegate {@code Finder}
467: * @param delegate the delegate finder
468: * @param mapper the mapper function
469: * @return the wrapped {@code Finder}
470: * @since 3.2-ALPHA-15
471: **********************************************************************************************************************************************************/
472: // START SNIPPET: mapping
473: @Nonnull
474: public static <U, V> Finder<U> mapping (@Nonnull final Finder<V> delegate, @Nonnull final Function<? super V, ? extends U> mapper)
475: // END SNIPPET: mapping
476: {
477: return new MappingFinder<>(delegate, mapper);
478: }
479: }