Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
BasicUtilities |
|
| 3.433333333333333;3.433 | ||||
BasicUtilities$1 |
|
| 3.433333333333333;3.433 | ||||
BasicUtilities$2 |
|
| 3.433333333333333;3.433 | ||||
BasicUtilities$3 |
|
| 3.433333333333333;3.433 | ||||
BasicUtilities$4 |
|
| 3.433333333333333;3.433 | ||||
BasicUtilities$5 |
|
| 3.433333333333333;3.433 | ||||
BasicUtilities$DetailsProvider |
|
| 3.433333333333333;3.433 | ||||
BasicUtilities$Validator |
|
| 3.433333333333333;3.433 |
1 | /* | |
2 | * Copyright (c) 2009, Swiss Federal Department of Defence Civil Protection and Sport | |
3 | * (http://www.vbs.admin.ch) | |
4 | * Copyright (c) 2009, Ergon Informatik AG (http://www.ergon.ch) | |
5 | * All rights reserved. | |
6 | * | |
7 | * Licensed under the Open Permis License which accompanies this distribution, | |
8 | * and is available at http://www.openpermis.org/BSDlicenceKent.txt | |
9 | */ | |
10 | package org.openpermis.policy.bean.basic; | |
11 | ||
12 | import java.util.ArrayList; | |
13 | import java.util.Iterator; | |
14 | import java.util.List; | |
15 | ||
16 | import org.openpermis.policy.Identifiable; | |
17 | import org.openpermis.policy.Nameable; | |
18 | import org.openpermis.policy.Part; | |
19 | import org.openpermis.policy.PartProblemReporter; | |
20 | import org.openpermis.policy.PartProblemReporter.ProblemMessage; | |
21 | import org.openpermis.policy.bean.PartBean; | |
22 | import org.openpermis.policy.bean.SerialNumber; | |
23 | ||
24 | /** | |
25 | * Helper functions for part implementations. | |
26 | * @since 0.1.0 | |
27 | */ | |
28 | public final class BasicUtilities { | |
29 | ||
30 | //---- Static | |
31 | ||
32 | /** | |
33 | * Seed value used when combining hash codes. | |
34 | * @see #multiHashCode(int...) | |
35 | * @since 0.1.0 | |
36 | */ | |
37 | private static final int MULTI_HASH_CODE_SEED = 17; | |
38 | ||
39 | /** | |
40 | * Multiplier used when combining hash codes. | |
41 | * @see #multiHashCode(int...) | |
42 | * @since 0.1.0 | |
43 | */ | |
44 | private static final int MULTI_HASH_CODE_MULTIPLIER = 37; | |
45 | ||
46 | /** | |
47 | * Constant used by list functions to indicate that no entry could be found. | |
48 | * @since 0.1.0 | |
49 | */ | |
50 | public static final int NOT_FOUND = -1; | |
51 | ||
52 | /** | |
53 | * Convenience function to create a hash code by combining several other hash code values. | |
54 | * <p>Use this method instead of combining multiple hash codes yourself. This method provides | |
55 | * a suitable distribution of hash codes.</p> | |
56 | * @param values the values to combine. | |
57 | * @return the combined hash code. | |
58 | * @since 0.1.0 | |
59 | */ | |
60 | public static final int multiHashCode (int... values) { | |
61 | 1576 | int result = MULTI_HASH_CODE_SEED; |
62 | 6296 | for (int value : values) { |
63 | 4720 | result = MULTI_HASH_CODE_MULTIPLIER * result + value; |
64 | } | |
65 | 1576 | return result; |
66 | } | |
67 | ||
68 | /** | |
69 | * Computes the hash code of a list without respecting the list order. | |
70 | * <p>Use if a list is used as if it were a {@link java.util.Set set} that allows multiple | |
71 | * objects that are {@link Object#equals(Object) equal}.</p> | |
72 | * @note Only use in conjunction with {@link #equalLists(List, List)}! | |
73 | * @param list the list for which to compute the hash code. | |
74 | * @return the computed hash code. | |
75 | * @see #equalLists(List, List) | |
76 | * @since 0.1.0 | |
77 | */ | |
78 | public static final int listHashCode (List<?> list) { | |
79 | 0 | int hash = 0; |
80 | 0 | final Iterator<?> i = list.iterator(); |
81 | 0 | while (i.hasNext()) { |
82 | 0 | final Object obj = i.next(); |
83 | 0 | if (obj != null) { |
84 | 0 | hash += obj.hashCode(); |
85 | } | |
86 | 0 | } |
87 | 0 | return hash; |
88 | } | |
89 | ||
90 | /** | |
91 | * Computes the hash code of a list without respecting the list order. | |
92 | * <p>Use if a list is used as if it were a {@link java.util.Set set} that allows multiple | |
93 | * objects that are {@link Object#equals(Object) equal}.</p> | |
94 | * @note Only use in conjunction with {@link #equalLists(List, List)}! | |
95 | * @param iterator the iterator for which to compute the hash code. | |
96 | * @return the computed hash code. | |
97 | * @see #equalLists(List, List) | |
98 | * @since 0.1.0 | |
99 | */ | |
100 | public static final int listHashCode (Iterator<?> iterator) { | |
101 | 24 | int hash = 0; |
102 | 48 | while (iterator.hasNext()) { |
103 | 24 | final Object obj = iterator.next(); |
104 | 24 | if (obj != null) { |
105 | 24 | hash += obj.hashCode(); |
106 | } | |
107 | 24 | } |
108 | 24 | return hash; |
109 | } | |
110 | ||
111 | /** | |
112 | * Compares two lists without respecting the list order. | |
113 | * <p>Use if a list is used as if it were a {@link java.util.Set set} that allows multiple | |
114 | * objects that are {@link Object#equals(Object) equal}.</p> | |
115 | * @note Only use in conjunction with {@link #listHashCode(List)}! | |
116 | * @param list1 the first list. | |
117 | * @param list2 the second list. | |
118 | * @return {@code true} if the two lists have an equivalent content, disregarding the order | |
119 | * of their entries. | |
120 | * @since 0.1.0 | |
121 | */ | |
122 | public static final boolean equalLists (List<?> list1, List<?> list2) { | |
123 | 47 | final List<?> temp = new ArrayList<Object>(list2); |
124 | 47 | for (Object entry : list1) { |
125 | 102 | final int pos = temp.indexOf(entry); |
126 | 102 | if (pos == -1) { |
127 | 0 | return false; |
128 | } | |
129 | 102 | temp.remove(pos); |
130 | 102 | } |
131 | 47 | return temp.isEmpty(); |
132 | } | |
133 | ||
134 | /** | |
135 | * Convenience function to test if two objects are both {@code null} or equivalent. | |
136 | * @param obj1 the first object. | |
137 | * @param obj2 the second object. | |
138 | * @return {@code true} if both objects are {@code null} or equivalent. | |
139 | * @since 0.1.0 | |
140 | */ | |
141 | public static final boolean equalObjects (Object obj1, Object obj2) { | |
142 | 1140 | if (obj1 == obj2) { |
143 | 74 | return true; |
144 | } | |
145 | 1066 | if (obj1 == null || obj2 == null) { |
146 | 0 | return false; |
147 | } | |
148 | 1066 | return obj1.equals(obj2); |
149 | } | |
150 | ||
151 | /** | |
152 | * Creates an illegal argument exception from the details specified. | |
153 | * @param details the exception cause. | |
154 | * @param pos the position to include. | |
155 | * @param type the type in which the cause was detected, may be {@code null}. | |
156 | * @return the exception created. | |
157 | * @since 0.1.0 | |
158 | */ | |
159 | private static final IllegalArgumentException createException ( | |
160 | String details, int pos, String type | |
161 | ) { | |
162 | 0 | final StringBuilder sb = new StringBuilder("Illegal ").append(details); |
163 | 0 | sb.append(" at position [").append(pos).append("]"); |
164 | 0 | if (type != null) { |
165 | 0 | sb.append(" in [").append(type).append("]"); |
166 | } | |
167 | 0 | sb.append("."); |
168 | 0 | return new IllegalArgumentException(sb.toString()); |
169 | } | |
170 | ||
171 | /** | |
172 | * Asserts that the list specified is legal. | |
173 | * <p>Lists are considered legal if the following conditions are met:</p> | |
174 | * <ul> | |
175 | * <li>They are not {@code null}.</li> | |
176 | * <li>They do not contain {@code null} entries.</li> | |
177 | * <li>All entries are unique (i.e. the same object is not included twice).</li> | |
178 | * <li>The list does not contain duplicate serial numbers.</li> | |
179 | * <li>All serial numbers share the same context as the owner (only checked if the | |
180 | * {@code owner} specified is not {@code null} and has a well-defined serial number).</li> | |
181 | * </ul> | |
182 | * @param <T> the type of the list to validate. | |
183 | * @param owner the part that owns (or will own) the list, may be {@code null}. | |
184 | * @param type the type of list (used in exceptions thrown), may be {@code null}. | |
185 | * @param list the list to validate. may be {@code null}. | |
186 | * @throws IllegalArgumentException thrown if any of the conditions listed above is violated. | |
187 | * @since 0.1.0 | |
188 | */ | |
189 | public static <T extends PartBean> void assertListIsLegal ( | |
190 | PartBean owner, String type, List<T> list | |
191 | ) | |
192 | throws IllegalArgumentException | |
193 | { | |
194 | 0 | if (list == null) { |
195 | 0 | throw new IllegalArgumentException("Illegal [" + type + "], must not be [null]."); |
196 | } | |
197 | 0 | int pos = findNullListEntry(list); |
198 | 0 | if (pos != NOT_FOUND) { |
199 | 0 | throw createException("[null] entry", pos, type); |
200 | } | |
201 | 0 | pos = findDuplicateListEntry(list); |
202 | 0 | if (pos != NOT_FOUND) { |
203 | 0 | throw createException("duplicate entry", pos, type); |
204 | } | |
205 | 0 | pos = findDuplicateSerialNumberInList(list); |
206 | 0 | if (pos != NOT_FOUND) { |
207 | 0 | throw createException("duplicate serial number of entry", pos, type); |
208 | } | |
209 | 0 | final SerialNumber ownerSerial = owner == null ? null : owner.getSerialNumber(); |
210 | 0 | if (ownerSerial != null && !ownerSerial.isUndefined()) { |
211 | 0 | pos = findInvalidSerialNumberInList(list, ownerSerial); |
212 | 0 | if (pos != NOT_FOUND) { |
213 | 0 | throw createException("serial number context of entry", pos, type); |
214 | } | |
215 | } | |
216 | 0 | } |
217 | ||
218 | /** | |
219 | * Finds the first entry which is {@code null}. | |
220 | * @param <T> the type of the list to search. | |
221 | * @param list the list to search. | |
222 | * @return the index of the first entry found, {@link #NOT_FOUND} if there is no such entry. | |
223 | * @since 0.1.0 | |
224 | */ | |
225 | public static <T> int findNullListEntry (List<T> list) { | |
226 | 0 | for (int i = 0; i < list.size(); i++) { |
227 | 0 | if (list.get(i) == null) { |
228 | 0 | return i; |
229 | } | |
230 | } | |
231 | 0 | return NOT_FOUND; |
232 | } | |
233 | ||
234 | /** | |
235 | * Finds the position of the first entry which is contained twice. | |
236 | * <p>Comparison is done by comparing the object identity ({@code ==}).</p> | |
237 | * @param <T> the type of the list to search. | |
238 | * @param list the list to search. | |
239 | * @return the index of the first entry found, {@link #NOT_FOUND} if there is no such entry. | |
240 | * @since 0.1.0 | |
241 | */ | |
242 | public static <T> int findDuplicateListEntry (List<T> list) { | |
243 | 0 | for (int i = 0; i < list.size(); i++) { |
244 | 0 | final T entry = list.get(i); |
245 | 0 | for (int k = 0; k < list.size(); k++) { |
246 | 0 | if (k == i) { |
247 | 0 | continue; |
248 | } | |
249 | 0 | if (list.get(k) == entry) { |
250 | 0 | return k; |
251 | } | |
252 | } | |
253 | } | |
254 | 0 | return NOT_FOUND; |
255 | } | |
256 | ||
257 | /** | |
258 | * Finds the position of the first entry with a duplicate serial number. | |
259 | * @param <T> the type of the list to search. | |
260 | * @param list the list to search. | |
261 | * @return the index of the first entry found, {@link #NOT_FOUND} if there is no such entry. | |
262 | * @since 0.1.0 | |
263 | */ | |
264 | public static <T extends PartBean> int findDuplicateSerialNumberInList (List<T> list) { | |
265 | 0 | for (int i = 0; i < list.size(); i++) { |
266 | 0 | final T e1 = list.get(i); |
267 | 0 | if (e1 != null) { |
268 | 0 | final SerialNumber s1 = e1.getSerialNumber(); |
269 | 0 | if (s1 != null && !s1.isUndefined()) { |
270 | 0 | for (int k = 0; k < list.size(); k++) { |
271 | 0 | if (k == i) { |
272 | 0 | continue; |
273 | } | |
274 | 0 | final T e2 = list.get(k); |
275 | 0 | if (e2 != null) { |
276 | 0 | final SerialNumber s2 = e2.getSerialNumber(); |
277 | 0 | if (s2 != null && !s2.isUndefined()) { |
278 | 0 | if (s1.equals(s2)) { |
279 | 0 | return k; |
280 | } | |
281 | } | |
282 | } | |
283 | } | |
284 | } | |
285 | } | |
286 | } | |
287 | 0 | return NOT_FOUND; |
288 | } | |
289 | ||
290 | /** | |
291 | * Finds the position of the first entry with an invalid serial number. | |
292 | * <p>An entry is considered to have an invalid serial number if:</p> | |
293 | * <ul> | |
294 | * <li>The serial number is {@code null}.</li> | |
295 | * <li>The serial number that does not share the context with the one specified.</li> | |
296 | * </ul> | |
297 | * <p>{@link SerialNumber#UNDEFINED} serial numbers are ignored.</p> | |
298 | * @param <T> the type of the list to search. | |
299 | * @param list the list to search. | |
300 | * @param context the serial number whose context is to be matched, must not be | |
301 | * {@code null} and must not be {@link SerialNumber#UNDEFINED}. | |
302 | * @return the index of the first entry found, {@link #NOT_FOUND} if there is no such entry. | |
303 | * @since 0.1.0 | |
304 | */ | |
305 | public static <T extends PartBean> int findInvalidSerialNumberInList ( | |
306 | List<T> list, SerialNumber context | |
307 | ) { | |
308 | 0 | for (int i = 0; i < list.size(); i++) { |
309 | 0 | final T entry = list.get(i); |
310 | 0 | if (entry != null) { |
311 | 0 | final SerialNumber serial = entry.getSerialNumber(); |
312 | 0 | if (serial == null) { |
313 | 0 | return i; |
314 | } | |
315 | 0 | if (!serial.isUndefined() && !context.equalContext(serial)) { |
316 | 0 | return i; |
317 | } | |
318 | } | |
319 | } | |
320 | 0 | return NOT_FOUND; |
321 | } | |
322 | ||
323 | /** | |
324 | * Primitive to validate entries in a list. | |
325 | * @param <T> the type of list to validate. | |
326 | * @param list the list to validate. | |
327 | * @param validator the validator to use. | |
328 | * @return {@code true} if the list is valid. | |
329 | * @since 0.1.0 | |
330 | */ | |
331 | private static <T> boolean validateList (List<T> list, Validator<T> validator) { | |
332 | boolean valid; | |
333 | 0 | valid = true; |
334 | 0 | for (int i = 0; i < list.size(); i++) { |
335 | 0 | final T e1 = list.get(i); |
336 | 0 | if (e1 != null) { |
337 | 0 | for (int k = 0; k < list.size(); k++) { |
338 | 0 | if (i == k) { |
339 | 0 | continue; |
340 | } | |
341 | 0 | final T e2 = list.get(k); |
342 | 0 | if (e2 != null) { |
343 | 0 | if (!validator.valid(e1, e2)) { |
344 | 0 | valid = false; |
345 | } | |
346 | } | |
347 | } | |
348 | } | |
349 | } | |
350 | 0 | return valid; |
351 | } | |
352 | ||
353 | /** | |
354 | * Validates all names in the specified list. | |
355 | * <p>Entries with duplicate names are reported using the specified reporter.</p> | |
356 | * @param <T> the type of list to validate. | |
357 | * @param reporter the reporter to use, may be {@code null}. | |
358 | * @param owner the owner used when reporting problems. | |
359 | * @param type the type used when reporting problems. | |
360 | * @param list the list to validate, must not be {@code null}. | |
361 | * @return {@code true} if there are no duplicate names in the list. | |
362 | * @since 0.1.0 | |
363 | */ | |
364 | public static <T extends Nameable> boolean validateListNames ( | |
365 | final PartProblemReporter reporter, final Part owner, final String type, List<T> list | |
366 | ) { | |
367 | 0 | return validateList( |
368 | list, | |
369 | new Validator<T>() { | |
370 | 0 | public boolean valid (Nameable n1, Nameable n2) { |
371 | 0 | if (equalObjects(n1.getName(), n2.getName())) { |
372 | 0 | if (reporter != null) { |
373 | 0 | reporter.reportProblem( |
374 | owner, ProblemMessage.duplicateName, type, n1.getName() | |
375 | ); | |
376 | } | |
377 | 0 | return false; |
378 | } | |
379 | 0 | return true; |
380 | } | |
381 | } | |
382 | ); | |
383 | } | |
384 | ||
385 | /** | |
386 | * Validates all identities in the specified list. | |
387 | * <p>Entries with duplicate identities are reported using the specified reporter.</p> | |
388 | * @param <T> the type of list to validate. | |
389 | * @param reporter the reporter to use, may be {@code null}. | |
390 | * @param owner the owner used when reporting problems. | |
391 | * @param type the type used when reporting problems. | |
392 | * @param list the list to validate, must not be {@code null}. | |
393 | * @return {@code true} if there are no duplicate identities in the list. | |
394 | * @since 0.1.0 | |
395 | */ | |
396 | public static <T extends Identifiable> boolean validateListIdentities ( | |
397 | final PartProblemReporter reporter, final Part owner, final String type, List<T> list | |
398 | ) { | |
399 | 0 | return validateList( |
400 | list, | |
401 | new Validator<T>() { | |
402 | 0 | public boolean valid (Identifiable id1, Identifiable id2) { |
403 | 0 | if (equalObjects(id1.getIdentity(), id2.getIdentity())) { |
404 | 0 | if (reporter != null) { |
405 | 0 | reporter.reportProblem( |
406 | owner, ProblemMessage.duplicateIdentity, type, id1.getIdentity() | |
407 | ); | |
408 | } | |
409 | 0 | return false; |
410 | } | |
411 | 0 | return true; |
412 | } | |
413 | } | |
414 | ); | |
415 | } | |
416 | ||
417 | /** | |
418 | * Returns the details for the specified collection using the provider passed. | |
419 | * <p>The collection is surrounded by curly brackets to indicate that it's a collection.</p> | |
420 | * @param <T> the type of collection to process. | |
421 | * @param collection the collection to process. | |
422 | * @param provider the provider to use for string details. | |
423 | * @return the string representation requested. | |
424 | * @since 0.1.0 | |
425 | */ | |
426 | public static <T> String getDetails (Iterable<T> collection, DetailsProvider<T> provider) { | |
427 | 0 | final StringBuilder sb = new StringBuilder("{"); |
428 | 0 | for (T item : collection) { |
429 | 0 | if (sb.length() > 1) { |
430 | 0 | sb.append(", "); |
431 | } | |
432 | 0 | sb.append(provider.details(item)); |
433 | } | |
434 | 0 | sb.append("}"); |
435 | 0 | return sb.toString(); |
436 | } | |
437 | ||
438 | /** | |
439 | * Returns the string details representation for the specified collection. | |
440 | * <p>The details are created using {@link Object#toString()} of the items.</p> | |
441 | * @param <T> the type of collection to process. | |
442 | * @param collection the collection to process. | |
443 | * @return the string representation requested. | |
444 | * @since 0.1.0 | |
445 | */ | |
446 | public static <T> String getStringDetails (Iterable<T> collection) { | |
447 | 0 | return getDetails(collection, new DetailsProvider<T>()); |
448 | } | |
449 | ||
450 | /** | |
451 | * Returns the string details representation for the specified collection. | |
452 | * <p>The details are created using {@link Object#toString()} of the items.</p> | |
453 | * @param <T> the type of collection to process. | |
454 | * @param collection the collection to process. | |
455 | * @return the string representation requested. | |
456 | * @since 0.1.0 | |
457 | */ | |
458 | public static <T extends PartBean> String getShortStringDetails (Iterable<T> collection) { | |
459 | 0 | return getDetails( |
460 | collection, | |
461 | new DetailsProvider<T>() { | |
462 | 0 | protected String provideDetails (T item) { |
463 | 0 | return new StringBuilder("["). |
464 | append(item.toShortString()).append("]").toString(); | |
465 | } | |
466 | } | |
467 | ); | |
468 | } | |
469 | ||
470 | /** | |
471 | * Returns the string details representation for the specified collection. | |
472 | * <p>The details are created using the {@link Identifiable#getIdentity()} of the items.</p> | |
473 | * @param <T> the type of collection to process. | |
474 | * @param collection the collection to process. | |
475 | * @return the string representation requested. | |
476 | * @since 0.1.0 | |
477 | */ | |
478 | public static <T extends Identifiable> String getIdentityDetails (Iterable<T> collection) { | |
479 | 0 | return getDetails( |
480 | collection, | |
481 | new DetailsProvider<T>() { | |
482 | 0 | protected String provideDetails (T item) { |
483 | 0 | return getIdentityDetails(item); |
484 | } | |
485 | } | |
486 | ); | |
487 | } | |
488 | ||
489 | /** | |
490 | * Returns the string details representation for the specified collection. | |
491 | * <p>The details are created using the {@link Nameable#getName()} of the items.</p> | |
492 | * @param <T> the type of collection to process. | |
493 | * @param collection the collection to process. | |
494 | * @return the string representation requested. | |
495 | * @since 0.3.0 | |
496 | */ | |
497 | public static <T extends Nameable> String getNameDetails (Iterable<T> collection) { | |
498 | 0 | return getDetails( |
499 | collection, | |
500 | new DetailsProvider<T>() { | |
501 | 0 | protected String provideDetails (T item) { |
502 | 0 | return getNameDetails(item); |
503 | } | |
504 | } | |
505 | ); | |
506 | } | |
507 | ||
508 | /** | |
509 | * Returns the identity of the specified part item. | |
510 | * <p>The value is returned in square brackets to indicate that it's an object.</p> | |
511 | * @param item the item to inspect, may be {@code null}. | |
512 | * @return the identity of the specified part item or the string representation of | |
513 | * {@code null} if the item specified is null. | |
514 | * @since 0.1.0 | |
515 | */ | |
516 | public static String getIdentityDetails (Identifiable item) { | |
517 | 0 | if (item == null) { |
518 | 0 | return String.valueOf(item); |
519 | } | |
520 | 0 | return new StringBuilder("[").append(item.getIdentity()).append("]").toString(); |
521 | } | |
522 | ||
523 | /** | |
524 | * Returns the name of the specified part item. | |
525 | * <p>The value is returned in curly brackets to indicate that it's an object.</p> | |
526 | * @param item the item to inspect, may be {@code null}. | |
527 | * @return the name of the specified part item or the string representation of | |
528 | * {@code null} if the item specified is null. | |
529 | * @since 0.1.0 | |
530 | */ | |
531 | public static String getNameDetails (Nameable item) { | |
532 | 0 | if (item == null) { |
533 | 0 | return String.valueOf(item); |
534 | } | |
535 | 0 | return new StringBuilder("[").append(item.getName()).append("]").toString(); |
536 | } | |
537 | ||
538 | //---- Constructors | |
539 | ||
540 | /** | |
541 | * Objects of this class cannot be instantiated. | |
542 | * @since 0.1.0 | |
543 | */ | |
544 | private BasicUtilities () { | |
545 | 0 | super(); |
546 | 0 | } |
547 | ||
548 | //---- Validator | |
549 | ||
550 | /** | |
551 | * Validator interface for {@link BasicUtilities#validateList(List, Validator)}. | |
552 | * @param <T> the type of the validator. | |
553 | * @since 0.1.0 | |
554 | */ | |
555 | private interface Validator<T> { | |
556 | ||
557 | /** | |
558 | * Validate the two entries by comparing them according to the validator function. | |
559 | * @param e1 the first entry. | |
560 | * @param e2 the second entry. | |
561 | * @return {@code true} if they are valid, {@code false} if they violate an invariant. | |
562 | * @since 0.1.0 | |
563 | */ | |
564 | public boolean valid (T e1, T e2); | |
565 | ||
566 | } | |
567 | ||
568 | //---- DetailsProvider | |
569 | ||
570 | /** | |
571 | * Provides string details about an item. | |
572 | * @param <T> the type of items processed. | |
573 | * @since 0.1.0 | |
574 | */ | |
575 | 0 | public static class DetailsProvider<T> { |
576 | ||
577 | /** | |
578 | * Provides the string details about the specified item. | |
579 | * <p>The default implementation returns its {@link Object#toString()} value.</p> | |
580 | * @param item the item for which to provide details, never {@code null}. | |
581 | * @return the details requested. | |
582 | * @since 0.1.0 | |
583 | */ | |
584 | protected String provideDetails (T item) { | |
585 | 0 | return String.valueOf(item); |
586 | } | |
587 | ||
588 | /** | |
589 | * Returns the string details of the specified item. | |
590 | * @param item the item for which to provide details, may be {@code null}. | |
591 | * @return the details requested. | |
592 | * @since 0.1.0 | |
593 | */ | |
594 | public final String details (T item) { | |
595 | 0 | if (item == null) { |
596 | 0 | return String.valueOf(item); |
597 | } | |
598 | 0 | return provideDetails(item); |
599 | } | |
600 | ||
601 | } | |
602 | ||
603 | } |