Coverage Report - org.openpermis.policy.bean.basic.BasicUtilities
 
Classes in this File Line Coverage Branch Coverage Complexity
BasicUtilities
17%
22/124
12%
12/100
3.433
BasicUtilities$1
0%
0/6
0%
0/4
3.433
BasicUtilities$2
0%
0/6
0%
0/4
3.433
BasicUtilities$3
0%
0/2
N/A
3.433
BasicUtilities$4
0%
0/2
N/A
3.433
BasicUtilities$5
0%
0/2
N/A
3.433
BasicUtilities$DetailsProvider
0%
0/5
0%
0/2
3.433
BasicUtilities$Validator
N/A
N/A
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  1558
                 int result = MULTI_HASH_CODE_SEED;
 62  6224
                 for (int value : values) {
 63  4666
                         result = MULTI_HASH_CODE_MULTIPLIER * result + value;
 64  
                 }
 65  1558
                 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  
 }