Clover coverage report - PLT Utilities Test Coverage (plt-20120304-r5436)
Coverage timestamp: Sat Mar 3 2012 22:01:56 CST
file stats: LOC: 935   Methods: 88
NCLOC: 500   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
RecurUtil.java 18.5% 21% 19.3% 20.3%
coverage coverage
 1    /*BEGIN_COPYRIGHT_BLOCK*
 2   
 3    PLT Utilities BSD License
 4   
 5    Copyright (c) 2007-2010 JavaPLT group at Rice University
 6    All rights reserved.
 7   
 8    Developed by: Java Programming Languages Team
 9    Rice University
 10    http://www.cs.rice.edu/~javaplt/
 11   
 12    Redistribution and use in source and binary forms, with or without modification, are permitted
 13    provided that the following conditions are met:
 14   
 15    - Redistributions of source code must retain the above copyright notice, this list of conditions
 16    and the following disclaimer.
 17    - Redistributions in binary form must reproduce the above copyright notice, this list of
 18    conditions and the following disclaimer in the documentation and/or other materials provided
 19    with the distribution.
 20    - Neither the name of the JavaPLT group, Rice University, nor the names of the library's
 21    contributors may be used to endorse or promote products derived from this software without
 22    specific prior written permission.
 23   
 24    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 25    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 26    FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND
 27    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 28    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 29    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 30    IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 31    OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 32   
 33    *END_COPYRIGHT_BLOCK*/
 34   
 35    package edu.rice.cs.plt.recur;
 36   
 37    import java.util.Arrays;
 38    import edu.rice.cs.plt.lambda.Lambda;
 39    import edu.rice.cs.plt.lambda.Lambda2;
 40    import edu.rice.cs.plt.lambda.Predicate2;
 41    import edu.rice.cs.plt.lambda.LambdaUtil;
 42    import edu.rice.cs.plt.collect.TotalMap;
 43    import edu.rice.cs.plt.reflect.ReflectUtil;
 44    import edu.rice.cs.plt.text.TextUtil;
 45   
 46    /**
 47    * TODO: Is the extra overhead required to check for infinite loops enough to justify non-checking
 48    * alternatives to safeToString, safeEquals, and safeHashCode?
 49    */
 50    public final class RecurUtil {
 51   
 52    private static final TotalMap<Thread, RecursionStack<Object>> TO_STRING_STACKS;
 53    private static final TotalMap<Thread, RecursionStack2<Object, Object>> EQUALS_STACKS;
 54    private static final TotalMap<Thread, RecursionStack<Object>> HASH_CODE_STACKS;
 55   
 56    private static final Lambda<ArrayStringMode, Lambda<Object, String>> TO_STRING_GENERATOR;
 57    private static final Lambda<ArrayStringMode, Lambda<Object, String>> DEFAULT_INF_STRING_GENERATOR;
 58    private static final Lambda2<Object, Object, Boolean> EQUALS;
 59    private static final Lambda<Object, Integer> HASH_CODE;
 60    private static final Lambda<Object, Integer> DEFAULT_INF_HASH_CODE;
 61   
 62    static {
 63  12 Lambda<Thread, RecursionStack<Object>> makeNew = new Lambda<Thread, RecursionStack<Object>>() {
 64  181 public RecursionStack<Object> value(Thread t) { return new RecursionStack<Object>(); }
 65    };
 66  12 Lambda<Thread, RecursionStack2<Object, Object>> makeNew2 =
 67    new Lambda<Thread, RecursionStack2<Object, Object>>() {
 68  388 public RecursionStack2<Object, Object> value(Thread t) {
 69  388 return new RecursionStack2<Object, Object>();
 70    }
 71    };
 72   
 73    // caching must be enabled (otherwise, a new stack is produced every time)
 74  12 TO_STRING_STACKS = new TotalMap<Thread, RecursionStack<Object>>(makeNew, true);
 75  12 EQUALS_STACKS = new TotalMap<Thread, RecursionStack2<Object, Object>>(makeNew2, true);
 76  12 HASH_CODE_STACKS = new TotalMap<Thread, RecursionStack<Object>>(makeNew, true);
 77   
 78  12 TO_STRING_GENERATOR = LambdaUtil.curry(new Lambda2<ArrayStringMode, Object, String>() {
 79  181 public String value(ArrayStringMode mode, Object obj) {
 80  3 if (obj.getClass().isArray()) { return arrayToString(obj, mode); }
 81  178 else { return obj.toString(); }
 82    }
 83    });
 84   
 85  12 DEFAULT_INF_STRING_GENERATOR = LambdaUtil.curry(new Lambda2<ArrayStringMode, Object, String>() {
 86  1 public String value(ArrayStringMode mode, Object obj) {
 87  1 if (obj.getClass().isArray()) { return mode.prefix() + "..." + mode.suffix(); }
 88  0 else { return ReflectUtil.simpleName(obj.getClass()) + "..."; }
 89    }
 90    });
 91   
 92  12 EQUALS = new Lambda2<Object, Object, Boolean>() {
 93  394 public Boolean value(Object obj1, Object obj2) {
 94  394 if (obj1.getClass().isArray()) {
 95  0 if (obj2.getClass().isArray()) { return arrayEquals(obj1, obj2); }
 96  0 else { return false; }
 97    }
 98    else {
 99  0 if (obj2.getClass().isArray()) { return false; }
 100  394 else { return obj1.equals(obj2); }
 101    }
 102    }
 103    };
 104   
 105  12 HASH_CODE = new Lambda<Object, Integer>() {
 106  0 public Integer value(Object obj) {
 107  0 if (obj.getClass().isArray()) { return arrayHashCode(obj); }
 108  0 else { return obj.hashCode(); }
 109    }
 110    };
 111   
 112  12 DEFAULT_INF_HASH_CODE = LambdaUtil.valueLambda(0xB32FC891);
 113    }
 114   
 115    /** Prevents instance creation */
 116  0 private RecurUtil() {}
 117   
 118    /**
 119    * Invokes {@link #safeToString(Object, Lambda, int, ArrayStringMode)} using a default
 120    * {@code infiniteString} like {@code "ClassName..."} or <code>"{ ... }"</code>, a default
 121    * {@code depth} of {@code 1}, and {@link ArrayStringMode#DEEP_BRACED} as a default array
 122    * string mode.
 123    */
 124  182 public static String safeToString(Object obj) {
 125  182 return safeToString(obj, DEFAULT_INF_STRING_GENERATOR.value(ArrayStringMode.DEEP_BRACED),
 126    1, ArrayStringMode.DEEP_BRACED);
 127    }
 128   
 129    /**
 130    * Invokes {@link #safeToString(Object, Lambda, int, ArrayStringMode)} using the given string as
 131    * the {@code infiniteString}, a default {@code depth} of {@code 1}, and
 132    * {@link ArrayStringMode#DEEP_BRACED} as a default array string mode.
 133    */
 134  0 public static String safeToString(Object obj, String infiniteString) {
 135  0 return safeToString(obj, infiniteString, 1, ArrayStringMode.DEEP_BRACED);
 136    }
 137   
 138    /**
 139    * Invokes {@link #safeToString(Object, Lambda, int, ArrayStringMode)} with a default
 140    * {@code depth} of {@code 1}, and using {@link ArrayStringMode#DEEP_BRACED} as a default array
 141    * string mode.
 142    */
 143  0 public static <T> String safeToString(T obj, Lambda<? super T, String> infiniteString) {
 144  0 return safeToString(obj, infiniteString, 1, ArrayStringMode.DEEP_BRACED);
 145    }
 146   
 147    /**
 148    * Invokes {@link #safeToString(Object, Lambda, int, ArrayStringMode)} using a default
 149    * {@code infiniteString} like {@code "ClassName..."} or <code>"{ ... }"</code>, and a default
 150    * {@code depth} of {@code 1}.
 151    */
 152  8 public static String safeToString(Object obj, ArrayStringMode arrayMode) {
 153  8 return safeToString(obj, DEFAULT_INF_STRING_GENERATOR.value(arrayMode), 1, arrayMode);
 154    }
 155   
 156    /**
 157    * Invokes {@link #safeToString(Object, Lambda, int, ArrayStringMode)} using the given
 158    * string as the {@code infiniteString}, and a default {@code depth} of {@code 1}.
 159    */
 160  0 public static String safeToString(Object obj, String infiniteString, ArrayStringMode arrayMode) {
 161  0 return safeToString(obj, LambdaUtil.valueLambda(infiniteString), 1, arrayMode);
 162    }
 163   
 164    /**
 165    * Invokes {@link #safeToString(Object, Lambda, int, ArrayStringMode)} using a default
 166    * {@code depth} of {@code 1}.
 167    */
 168  0 public static <T> String safeToString(T obj, Lambda<? super T, String> infiniteString,
 169    ArrayStringMode arrayMode) {
 170  0 return safeToString(obj, infiniteString, 1, arrayMode);
 171    }
 172   
 173    /**
 174    * Invokes {@link #safeToString(Object, Lambda, int, ArrayStringMode)} using a default
 175    * {@code infiniteString} like {@code "ClassName..."} or <code>"{ ... }"</code>, and
 176    * {@link ArrayStringMode#DEEP_BRACED} as a default array string mode.
 177    */
 178  0 public static String safeToString(Object obj, int depth) {
 179  0 return safeToString(obj, DEFAULT_INF_STRING_GENERATOR.value(ArrayStringMode.DEEP_BRACED),
 180    depth, ArrayStringMode.DEEP_BRACED);
 181    }
 182   
 183    /**
 184    * Invokes {@link #safeToString(Object, Lambda, int, ArrayStringMode)} using the given string as
 185    * the {@code infiniteString}, and {@link ArrayStringMode#DEEP_BRACED} as a default array string
 186    * mode.
 187    */
 188  0 public static String safeToString(Object obj, String infiniteString, int depth) {
 189  0 return safeToString(obj, infiniteString, depth, ArrayStringMode.DEEP_BRACED);
 190    }
 191   
 192    /**
 193    * Invokes {@link #safeToString(Object, Lambda, int, ArrayStringMode)} using
 194    * {@link ArrayStringMode#DEEP_BRACED} as a default array string mode.
 195    */
 196  0 public static <T> String safeToString(T obj, Lambda<? super T, String> infiniteString, int depth) {
 197  0 return safeToString(obj, infiniteString, depth, ArrayStringMode.DEEP_BRACED);
 198    }
 199   
 200    /**
 201    * Invokes {@link #safeToString(Object, Lambda, int, ArrayStringMode)} using a default
 202    * {@code infiniteString} like {@code "ClassName..."} or <code>"{ ... }"</code>.
 203    */
 204  0 public static String safeToString(Object obj, int depth, ArrayStringMode arrayMode) {
 205  0 return safeToString(obj, DEFAULT_INF_STRING_GENERATOR.value(arrayMode), depth, arrayMode);
 206    }
 207   
 208    /**
 209    * Invokes {@link #safeToString(Object, Lambda, int, ArrayStringMode)} using the given
 210    * string as the {@code infiniteString}.
 211    */
 212  0 public static String safeToString(Object obj, String infiniteString, int depth,
 213    ArrayStringMode arrayMode) {
 214  0 return safeToString(obj, LambdaUtil.valueLambda(infiniteString), depth, arrayMode);
 215    }
 216   
 217    /**
 218    * <p>Evaluate {@code obj.toString()} under the protection of an infinite-recursion check.
 219    * If a string for {@code obj} is already in the process of being computed by this method
 220    * {@code depth} times (that is, this is the {@code depth+1}st nested invocation),
 221    * {@code infiniteString} will be applied. Otherwise, the result is {@code obj.toString()}.</p>
 222    *
 223    * <p>To simplify client code, this method also handles {@code null} values (returning
 224    * {@code "null"}) and arrays (calling {@link #arrayToString(Object, ArrayStringMode)}).</p>
 225    *
 226    * @param obj An object (may be {@code null} or an array)
 227    * @param infiniteString A lambda to generate a string for {@code obj} if its {@code toString}
 228    * method has already been invoked {@code depth} times
 229    * @param depth The number of times to allow a {@code toString} invocation before using
 230    * {@code infiniteString} instead
 231    * @param arrayMode The {@link ArrayStringMode} to use if {@code obj} is an array
 232    * @return A string representation of {@code obj}
 233    */
 234  190 public static <T> String safeToString(T obj, Lambda<? super T, String> infiniteString, int depth,
 235    ArrayStringMode arrayMode) {
 236  8 if (obj == null) { return "null"; }
 237    else {
 238  182 RecursionStack<Object> stack;
 239  182 Thread t = Thread.currentThread();
 240  182 synchronized (TO_STRING_STACKS) { stack = TO_STRING_STACKS.get(t); }
 241    // no synchronization on stack, because it is only used in this thread
 242  182 String result = stack.<T, String>apply(TO_STRING_GENERATOR.value(arrayMode), infiniteString, obj);
 243  182 if (stack.isEmpty()) {
 244  181 synchronized (TO_STRING_STACKS) { TO_STRING_STACKS.revert(t); }
 245    }
 246  182 return result;
 247    }
 248    }
 249   
 250    /**
 251    * Invoke {@link #arrayToString(Object, ArrayStringMode)} with a default string mode
 252    * {@link ArrayStringMode#DEEP_BRACED}
 253    */
 254  0 public static String arrayToString(Object array) {
 255  0 return arrayToString(array, ArrayStringMode.DEEP_BRACED);
 256    }
 257   
 258    /**
 259    * Generate a string representation of the given array. If the {@code stringMode} is
 260    * {@link ArrayStringMode#CLASS_NAME} or {@link ArrayStringMode#TYPE_AND_SIZE}, generate
 261    * a simple name, as specified; otherwise, generate a list of each of the elements (calling
 262    * {@link #safeToString(Object, ArrayStringMode)} where applicable, using the mode
 263    * produced by {@link ArrayStringMode#nestedMode()}).
 264    *
 265    * @throws IllegalArgumentException If {@code array} is not an array
 266    */
 267  3 public static String arrayToString(Object array, ArrayStringMode stringMode) {
 268  2 if (array instanceof Object[]) { return arrayToString((Object[]) array, stringMode); }
 269  1 else if (array instanceof int[]) { return arrayToString((int[]) array, stringMode); }
 270  0 else if (array instanceof char[]) { return arrayToString((char[]) array, stringMode); }
 271  0 else if (array instanceof byte[]) { return arrayToString((byte[]) array, stringMode); }
 272  0 else if (array instanceof double[]) { return arrayToString((double[]) array, stringMode); }
 273  0 else if (array instanceof boolean[]) { return arrayToString((boolean[]) array, stringMode); }
 274  0 else if (array instanceof short[]) { return arrayToString((short[]) array, stringMode); }
 275  0 else if (array instanceof long[]) { return arrayToString((long[]) array, stringMode); }
 276  0 else if (array instanceof float[]) { return arrayToString((float[]) array, stringMode); }
 277  0 else { throw new IllegalArgumentException("Non-array argument"); }
 278    }
 279   
 280    /**
 281    * Invoke {@link #arrayToString(Object[], ArrayStringMode)} with a default string mode
 282    * {@link ArrayStringMode#DEEP_BRACED}
 283    */
 284  0 public static String arrayToString(Object[] array) {
 285  0 return arrayToString(array, ArrayStringMode.DEEP_BRACED);
 286    }
 287   
 288    /**
 289    * Generate a string representation of the given array. If the {@code stringMode} is
 290    * {@link ArrayStringMode#CLASS_NAME} or {@link ArrayStringMode#TYPE_AND_SIZE}, generate
 291    * a simple name, as specified; otherwise call {@link #safeToString(Object, ArrayStringMode)}
 292    * on each of the elements, using the mode produced by {@link ArrayStringMode#nestedMode()}.
 293    * If {@code stringMode} is {@link ArrayStringMode#SHALLOW_BRACKETED}, the result will match
 294    * that of {@link Arrays#toString(Object[])}; if {@code stringMode} is
 295    * {@link ArrayStringMode#DEEP_BRACKETED}, the result will match that of
 296    * {@link Arrays#deepToString}.
 297    */
 298  2 public static String arrayToString(Object[] array, ArrayStringMode stringMode) {
 299  2 switch (stringMode) {
 300  0 case CLASS_NAME:
 301  0 return array.toString();
 302   
 303  0 case TYPE_AND_SIZE:
 304  0 return ReflectUtil.simpleName(array.getClass().getComponentType()) + "[" + array.length + "]";
 305   
 306  2 default:
 307  2 StringBuilder result = new StringBuilder();
 308  2 result.append(stringMode.prefix());
 309  2 boolean first = true;
 310  2 for (Object elt : array) {
 311  2 if (first) { first = false; }
 312  6 else { result.append(stringMode.delimiter()); }
 313  8 result.append(safeToString(elt, stringMode.nestedMode()));
 314    }
 315  2 result.append(stringMode.suffix());
 316  2 return result.toString();
 317    }
 318    }
 319   
 320    /**
 321    * Invoke {@link #arrayToString(boolean[], ArrayStringMode)} with a default string mode
 322    * {@link ArrayStringMode#DEEP_BRACED}
 323    */
 324  0 public static String arrayToString(boolean[] array) {
 325  0 return arrayToString(array, ArrayStringMode.DEEP_BRACED);
 326    }
 327   
 328    /**
 329    * Generate a string representation of the given array. If the {@code stringMode} is
 330    * {@link ArrayStringMode#CLASS_NAME} or {@link ArrayStringMode#TYPE_AND_SIZE}, generate
 331    * a simple name, as specified; otherwise, generate a list containing each element.
 332    */
 333  0 public static String arrayToString(boolean[] array, ArrayStringMode stringMode) {
 334  0 switch (stringMode) {
 335  0 case CLASS_NAME:
 336  0 return array.toString();
 337   
 338  0 case TYPE_AND_SIZE:
 339  0 return "boolean[" + array.length + "]";
 340   
 341  0 default:
 342  0 StringBuilder result = new StringBuilder();
 343  0 result.append(stringMode.prefix());
 344  0 boolean first = true;
 345  0 for (boolean elt : array) {
 346  0 if (first) { first = false; }
 347  0 else { result.append(stringMode.delimiter()); }
 348  0 result.append(elt);
 349    }
 350  0 result.append(stringMode.suffix());
 351  0 return result.toString();
 352    }
 353    }
 354   
 355    /**
 356    * Invoke {@link #arrayToString(char[], ArrayStringMode)} with a default string mode
 357    * {@link ArrayStringMode#DEEP_BRACED}
 358    */
 359  0 public static String arrayToString(char[] array) {
 360  0 return arrayToString(array, ArrayStringMode.DEEP_BRACED);
 361    }
 362   
 363    /**
 364    * Generate a string representation of the given array. If the {@code stringMode} is
 365    * {@link ArrayStringMode#CLASS_NAME} or {@link ArrayStringMode#TYPE_AND_SIZE}, generate
 366    * a simple name, as specified; otherwise, generate a list containing each element.
 367    */
 368  0 public static String arrayToString(char[] array, ArrayStringMode stringMode) {
 369  0 switch (stringMode) {
 370  0 case CLASS_NAME:
 371  0 return array.toString();
 372   
 373  0 case TYPE_AND_SIZE:
 374  0 return "char[" + array.length + "]";
 375   
 376  0 default:
 377  0 StringBuilder result = new StringBuilder();
 378  0 result.append(stringMode.prefix());
 379  0 boolean first = true;
 380  0 for (char elt : array) {
 381  0 if (first) { first = false; }
 382  0 else { result.append(stringMode.delimiter()); }
 383  0 result.append(elt);
 384    }
 385  0 result.append(stringMode.suffix());
 386  0 return result.toString();
 387    }
 388    }
 389   
 390    /**
 391    * Invoke {@link #arrayToString(byte[], ArrayStringMode)} with a default string mode
 392    * {@link ArrayStringMode#DEEP_BRACED}
 393    */
 394  0 public static String arrayToString(byte[] array) {
 395  0 return arrayToString(array, ArrayStringMode.DEEP_BRACED);
 396    }
 397   
 398    /**
 399    * Generate a string representation of the given array. If the {@code stringMode} is
 400    * {@link ArrayStringMode#CLASS_NAME} or {@link ArrayStringMode#TYPE_AND_SIZE}, generate
 401    * a simple name, as specified; otherwise, generate a list containing each element.
 402    */
 403  0 public static String arrayToString(byte[] array, ArrayStringMode stringMode) {
 404  0 switch (stringMode) {
 405  0 case CLASS_NAME:
 406  0 return array.toString();
 407   
 408  0 case TYPE_AND_SIZE:
 409  0 return "byte[" + array.length + "]";
 410   
 411  0 default:
 412  0 StringBuilder result = new StringBuilder();
 413  0 result.append(stringMode.prefix());
 414  0 boolean first = true;
 415  0 for (byte elt : array) {
 416  0 if (first) { first = false; }
 417  0 else { result.append(stringMode.delimiter()); }
 418  0 result.append(elt);
 419    }
 420  0 result.append(stringMode.suffix());
 421  0 return result.toString();
 422    }
 423    }
 424   
 425    /**
 426    * Invoke {@link #arrayToString(short[], ArrayStringMode)} with a default string mode
 427    * {@link ArrayStringMode#DEEP_BRACED}
 428    */
 429  0 public static String arrayToString(short[] array) {
 430  0 return arrayToString(array, ArrayStringMode.DEEP_BRACED);
 431    }
 432   
 433    /**
 434    * Generate a string representation of the given array. If the {@code stringMode} is
 435    * {@link ArrayStringMode#CLASS_NAME} or {@link ArrayStringMode#TYPE_AND_SIZE}, generate
 436    * a simple name, as specified; otherwise, generate a list containing each element.
 437    */
 438  0 public static String arrayToString(short[] array, ArrayStringMode stringMode) {
 439  0 switch (stringMode) {
 440  0 case CLASS_NAME:
 441  0 return array.toString();
 442   
 443  0 case TYPE_AND_SIZE:
 444  0 return "short[" + array.length + "]";
 445   
 446  0 default:
 447  0 StringBuilder result = new StringBuilder();
 448  0 result.append(stringMode.prefix());
 449  0 boolean first = true;
 450  0 for (short elt : array) {
 451  0 if (first) { first = false; }
 452  0 else { result.append(stringMode.delimiter()); }
 453  0 result.append(elt);
 454    }
 455  0 result.append(stringMode.suffix());
 456  0 return result.toString();
 457    }
 458    }
 459   
 460    /**
 461    * Invoke {@link #arrayToString(int[], ArrayStringMode)} with a default string mode
 462    * {@link ArrayStringMode#DEEP_BRACED}
 463    */
 464  0 public static String arrayToString(int[] array) {
 465  0 return arrayToString(array, ArrayStringMode.DEEP_BRACED);
 466    }
 467   
 468    /**
 469    * Generate a string representation of the given array. If the {@code stringMode} is
 470    * {@link ArrayStringMode#CLASS_NAME} or {@link ArrayStringMode#TYPE_AND_SIZE}, generate
 471    * a simple name, as specified; otherwise, generate a list containing each element.
 472    */
 473  1 public static String arrayToString(int[] array, ArrayStringMode stringMode) {
 474  1 switch (stringMode) {
 475  0 case CLASS_NAME:
 476  0 return array.toString();
 477   
 478  0 case TYPE_AND_SIZE:
 479  0 return "int[" + array.length + "]";
 480   
 481  1 default:
 482  1 StringBuilder result = new StringBuilder();
 483  1 result.append(stringMode.prefix());
 484  1 boolean first = true;
 485  1 for (int elt : array) {
 486  1 if (first) { first = false; }
 487  2 else { result.append(stringMode.delimiter()); }
 488  3 result.append(elt);
 489    }
 490  1 result.append(stringMode.suffix());
 491  1 return result.toString();
 492    }
 493    }
 494   
 495    /**
 496    * Invoke {@link #arrayToString(long[], ArrayStringMode)} with a default string mode
 497    * {@link ArrayStringMode#DEEP_BRACED}
 498    */
 499  0 public static String arrayToString(long[] array) {
 500  0 return arrayToString(array, ArrayStringMode.DEEP_BRACED);
 501    }
 502   
 503    /**
 504    * Generate a string representation of the given array. If the {@code stringMode} is
 505    * {@link ArrayStringMode#CLASS_NAME} or {@link ArrayStringMode#TYPE_AND_SIZE}, generate
 506    * a simple name, as specified; otherwise, generate a list containing each element.
 507    */
 508  0 public static String arrayToString(long[] array, ArrayStringMode stringMode) {
 509  0 switch (stringMode) {
 510  0 case CLASS_NAME:
 511  0 return array.toString();
 512   
 513  0 case TYPE_AND_SIZE:
 514  0 return "long[" + array.length + "]";
 515   
 516  0 default:
 517  0 StringBuilder result = new StringBuilder();
 518  0 result.append(stringMode.prefix());
 519  0 boolean first = true;
 520  0 for (long elt : array) {
 521  0 if (first) { first = false; }
 522  0 else { result.append(stringMode.delimiter()); }
 523  0 result.append(elt);
 524    }
 525  0 result.append(stringMode.suffix());
 526  0 return result.toString();
 527    }
 528    }
 529   
 530    /**
 531    * Invoke {@link #arrayToString(float[], ArrayStringMode)} with a default string mode
 532    * {@link ArrayStringMode#DEEP_BRACED}
 533    */
 534  0 public static String arrayToString(float[] array) {
 535  0 return arrayToString(array, ArrayStringMode.DEEP_BRACED);
 536    }
 537   
 538    /**
 539    * Generate a string representation of the given array. If the {@code stringMode} is
 540    * {@link ArrayStringMode#CLASS_NAME} or {@link ArrayStringMode#TYPE_AND_SIZE}, generate
 541    * a simple name, as specified; otherwise, generate a list containing each element.
 542    */
 543  0 public static String arrayToString(float[] array, ArrayStringMode stringMode) {
 544  0 switch (stringMode) {
 545  0 case CLASS_NAME:
 546  0 return array.toString();
 547   
 548  0 case TYPE_AND_SIZE:
 549  0 return "float[" + array.length + "]";
 550   
 551  0 default:
 552  0 StringBuilder result = new StringBuilder();
 553  0 result.append(stringMode.prefix());
 554  0 boolean first = true;
 555  0 for (float elt : array) {
 556  0 if (first) { first = false; }
 557  0 else { result.append(stringMode.delimiter()); }
 558  0 result.append(elt);
 559    }
 560  0 result.append(stringMode.suffix());
 561  0 return result.toString();
 562    }
 563    }
 564   
 565    /**
 566    * Invoke {@link #arrayToString(double[], ArrayStringMode)} with a default string mode
 567    * {@link ArrayStringMode#DEEP_BRACED}
 568    */
 569  0 public static String arrayToString(double[] array) {
 570  0 return arrayToString(array, ArrayStringMode.DEEP_BRACED);
 571    }
 572   
 573    /**
 574    * Generate a string representation of the given array. If the {@code stringMode} is
 575    * {@link ArrayStringMode#CLASS_NAME} or {@link ArrayStringMode#TYPE_AND_SIZE}, generate
 576    * a simple name, as specified; otherwise, generate a list containing each element.
 577    */
 578  0 public static String arrayToString(double[] array, ArrayStringMode stringMode) {
 579  0 switch (stringMode) {
 580  0 case CLASS_NAME:
 581  0 return array.toString();
 582   
 583  0 case TYPE_AND_SIZE:
 584  0 return "double[" + array.length + "]";
 585   
 586  0 default:
 587  0 StringBuilder result = new StringBuilder();
 588  0 result.append(stringMode.prefix());
 589  0 boolean first = true;
 590  0 for (double elt : array) {
 591  0 if (first) { first = false; }
 592  0 else { result.append(stringMode.delimiter()); }
 593  0 result.append(elt);
 594    }
 595  0 result.append(stringMode.suffix());
 596  0 return result.toString();
 597    }
 598    }
 599   
 600    /**
 601    * Invokes {@link #safeEquals(Object, Object, Predicate2)} using {@code true} for
 602    * {@code infiniteEquals} (note that {@code false} is not a reasonable value for
 603    * {@code infiniteEquals}, since an infinite structure must be equal to itself)
 604    *
 605    */
 606  394 public static boolean safeEquals(Object obj1, Object obj2) {
 607  394 return safeEquals(obj1, obj2, LambdaUtil.TRUE);
 608    }
 609   
 610    /**
 611    * <p>Evaluate {@code obj1.equals(obj2)} under the protection of an infinite-recursion check.
 612    * If the equality of {@code obj1} and {@code obj2} is already in the process of being computed
 613    * by this method, {@code infiniteEquals} will be applied. Otherwise, the result is
 614    * {@code obj1.equals(obj2)}.</p>
 615    *
 616    * <p>To simplify client code, this method also handles {@code null} values (equal iff both
 617    * values are {@code null}) and arrays (calling {@link #arrayEquals(Object, Object)}.</p>
 618    *
 619    * @param obj1 An object (may be {@code null} or an array)
 620    * @param obj2 An object (may be {@code null} or an array)
 621    * @param infiniteEquals A predicate to determine equality of {@code obj1.equals(obj2)} has
 622    * already been invoked
 623    * @return {@code true} iff the two objects are equal
 624    */
 625  394 public static <T1, T2> boolean safeEquals(T1 obj1, T2 obj2,
 626    Predicate2<? super T1, ? super T2> infiniteEquals) {
 627  0 if (obj1 == null) { return obj2 == null; }
 628  0 else if (obj2 == null) { return false; }
 629    else {
 630  394 RecursionStack2<Object, Object> stack;
 631  394 Thread t = Thread.currentThread();
 632  394 synchronized (EQUALS_STACKS) { stack = EQUALS_STACKS.get(t); }
 633    // no synchronization on stack, because it is only used in this thread
 634  394 boolean result = stack.<T1, T2, Boolean>apply(EQUALS, LambdaUtil.asLambda(infiniteEquals), obj1, obj2);
 635  394 if (stack.isEmpty()) {
 636  388 synchronized (EQUALS_STACKS) { EQUALS_STACKS.revert(t); }
 637    }
 638  394 return result;
 639    }
 640    }
 641   
 642    /**
 643    * Test the equality of the given arrays. The result is calculated by comparing the lengths,
 644    * types (in the primitive cases), and corresponding elements of the arguments, recurring on
 645    * any nested objects (including arrays) using {@link #safeEquals(Object, Object)}. Unlike
 646    * {@link Arrays#deepEquals}, this method is able to handle an array nested within
 647    * itself.
 648    *
 649    * @throws IllegalArgumentException If {@code array1} or {@code array2} is not an array
 650    */
 651  0 public static boolean arrayEquals(Object a1, Object a2) {
 652  0 if (!a1.getClass().isArray() || !a2.getClass().isArray()) {
 653  0 throw new IllegalArgumentException("Non-array argument");
 654    }
 655  0 if (a1 instanceof Object[] && a2 instanceof Object[]) {
 656  0 return arrayEquals((Object[]) a1, (Object[]) a2);
 657    }
 658  0 else if (!a1.getClass().equals(a2.getClass())) { return false; }
 659  0 else if (a1 instanceof int[]) { return Arrays.equals((int[]) a1, (int[]) a2); }
 660  0 else if (a1 instanceof char[]) { return Arrays.equals((char[]) a1, (char[]) a2); }
 661  0 else if (a1 instanceof byte[]) { return Arrays.equals((byte[]) a1, (byte[]) a2); }
 662  0 else if (a1 instanceof double[]) { return Arrays.equals((double[]) a1, (double[]) a2); }
 663  0 else if (a1 instanceof boolean[]) { return Arrays.equals((boolean[]) a1, (boolean[]) a2); }
 664  0 else if (a1 instanceof short[]) { return Arrays.equals((short[]) a1, (short[]) a2); }
 665  0 else if (a1 instanceof long[]) { return Arrays.equals((long[]) a1, (long[]) a2); }
 666  0 else if (a1 instanceof float[]) { return Arrays.equals((float[]) a1, (float[]) a2); }
 667  0 else { throw new IllegalArgumentException("Unrecognized array type"); }
 668    }
 669   
 670    /**
 671    * Test the equality of the given arrays. The result is calculated by comparing the lengths
 672    * and corresponding elements of the arguments, recurring on nested objects (including arrays)
 673    * using {@link #safeEquals(Object, Object)}. Note that arrays of different runtime types
 674    * may still be equal if their elements are equal. Unlike {@link Arrays#deepEquals}, this
 675    * method is able to handle an array nested within itself.
 676    */
 677  0 public static boolean arrayEquals(Object[] a1, Object[] a2) {
 678  0 if (a1.length != a2.length) { return false; }
 679  0 for (int i = 0; i < a1.length; i++) { if (!safeEquals(a1[i], a2[i])) { return false; } }
 680  0 return true;
 681    }
 682   
 683   
 684    /**
 685    * Invokes {@link #safeHashCode(Object, Lambda)} using an arbitrary default value as the
 686    * {@code infiniteHashCode}.
 687    */
 688  0 public static int safeHashCode(Object obj) {
 689  0 return safeHashCode(obj, DEFAULT_INF_HASH_CODE);
 690    }
 691   
 692    /**
 693    * Invokes {@link #safeHashCode(Object, Lambda)} using the given value as the
 694    * {@code infiniteHashCode}.
 695    */
 696  0 public static int safeHashCode(Object obj, int infiniteHashCode) {
 697  0 return safeHashCode(obj, LambdaUtil.valueLambda(infiniteHashCode));
 698    }
 699   
 700    /**
 701    * <p>Evaluate {@code obj.hashCode()} under the protection of an infinite-recursion check.
 702    * If the hash code for {@code obj} is already in the process of being computed by this method,
 703    * {@code infiniteHashCode} will be applied. Otherwise, the result is {@code obj.hashCode()}.</p>
 704    *
 705    * <p>To simplify client code, this method also handles {@code null} values (returning {@code 0})
 706    * and arrays (calling {@link #arrayHashCode(Object)}).</p>
 707    *
 708    * @param obj An object (may be {@code null} or an array)
 709    * @param infiniteHashCode A lambda to generate a hash code for {@code obj} if its
 710    * {@code hashCode} method has already been invoked
 711    * @return A hash code for of {@code obj}
 712    */
 713  0 public static <T> int safeHashCode(T obj, Lambda<? super T, Integer> infiniteHashCode) {
 714  0 if (obj == null) { return 0; }
 715    else {
 716  0 RecursionStack<Object> stack;
 717  0 Thread t = Thread.currentThread();
 718  0 synchronized (HASH_CODE_STACKS) { stack = HASH_CODE_STACKS.get(t); }
 719    // no synchronization on stack, because it is only used in this thread
 720  0 int result = stack.<T, Integer>apply(HASH_CODE, infiniteHashCode, obj);
 721  0 if (stack.isEmpty()) {
 722  0 synchronized (HASH_CODE_STACKS) { HASH_CODE_STACKS.revert(t); }
 723    }
 724  0 return result;
 725    }
 726    }
 727   
 728    /**
 729    * Generate a hash code for the given array. The result is calculated as specified by
 730    * {@link java.util.List#hashCode}, recurring on any nested objects (including arrays)
 731    * using {@link #safeHashCode(Object)}. Unlike {@link Arrays#deepHashCode},
 732    * this method is able to handle an array nested within itself.
 733    *
 734    * @throws IllegalArgumentException If {@code array} is not an array
 735    */
 736  0 public static int arrayHashCode(Object array) {
 737  0 if (array instanceof Object[]) { return arrayHashCode((Object[]) array); }
 738  0 else if (array instanceof int[]) { return arrayHashCode((int[]) array); }
 739  0 else if (array instanceof char[]) { return arrayHashCode((char[]) array); }
 740  0 else if (array instanceof byte[]) { return arrayHashCode((byte[]) array); }
 741  0 else if (array instanceof double[]) { return arrayHashCode((double[]) array); }
 742  0 else if (array instanceof boolean[]) { return arrayHashCode((boolean[]) array); }
 743  0 else if (array instanceof short[]) { return arrayHashCode((short[]) array); }
 744  0 else if (array instanceof long[]) { return arrayHashCode((long[]) array); }
 745  0 else if (array instanceof float[]) { return arrayHashCode((float[]) array); }
 746  0 else { throw new IllegalArgumentException("Non-array argument"); }
 747    }
 748   
 749    /**
 750    * Generate a hash code for the given array. The result is calculated as specified by
 751    * {@link java.util.List#hashCode}, recurring on each element using {@link #safeHashCode(Object)}.
 752    * Unlike {@link Arrays#deepHashCode}, this method is able to handle an array nested
 753    * within itself.
 754    */
 755  0 public static int arrayHashCode(Object[] array) {
 756  0 int result = 1;
 757  0 for (Object elt : array) { result = result*31 + safeHashCode(elt); }
 758  0 return result;
 759    }
 760   
 761    /**
 762    * Generate a hash code for the given array. The result is calculated as specified by
 763    * {@link java.util.List#hashCode}, invoking the {@code Boolean#hashCode} method on
 764    * each element. (Note that {@link Arrays#hashCode(boolean[])} implements the
 765    * same method; this is defined here for compatibility with earlier APIs.)
 766    */
 767  0 public static int arrayHashCode(boolean[] array) {
 768  0 int result = 1;
 769  0 for (boolean elt : array) { result = result*31 + ((Boolean) elt).hashCode(); }
 770  0 return result;
 771    }
 772   
 773    /**
 774    * Generate a hash code for the given array. The result is calculated as specified by
 775    * {@link java.util.List#hashCode}, invoking the {@code Character#hashCode} method on
 776    * each element. (Note that {@link Arrays#hashCode(char[])} implements the
 777    * same method; this is defined here for compatibility with earlier APIs.)
 778    */
 779  0 public static int arrayHashCode(char[] array) {
 780  0 int result = 1;
 781  0 for (char elt : array) { result = result*31 + ((Character) elt).hashCode(); }
 782  0 return result;
 783    }
 784   
 785    /**
 786    * Generate a hash code for the given array. The result is calculated as specified by
 787    * {@link java.util.List#hashCode}, invoking the {@code Byte#hashCode} method on
 788    * each element. (Note that {@link Arrays#hashCode(byte[])} implements the
 789    * same method; this is defined here for compatibility with earlier APIs.)
 790    */
 791  0 public static int arrayHashCode(byte[] array) {
 792  0 int result = 1;
 793  0 for (byte elt : array) { result = result*31 + ((Byte) elt).hashCode(); }
 794  0 return result;
 795    }
 796   
 797    /**
 798    * Generate a hash code for the given array. The result is calculated as specified by
 799    * {@link java.util.List#hashCode}, invoking the {@code Short#hashCode} method on
 800    * each element. (Note that {@link Arrays#hashCode(short[])} implements the
 801    * same method; this is defined here for compatibility with earlier APIs.)
 802    */
 803  0 public static int arrayHashCode(short[] array) {
 804  0 int result = 1;
 805  0 for (short elt : array) { result = result*31 + ((Short) elt).hashCode(); }
 806  0 return result;
 807    }
 808   
 809    /**
 810    * Generate a hash code for the given array. The result is calculated as specified by
 811    * {@link java.util.List#hashCode}, invoking the {@code Integer#hashCode} method on
 812    * each element. (Note that {@link Arrays#hashCode(int[])} implements the
 813    * same method; this is defined here for compatibility with earlier APIs.)
 814    */
 815  0 public static int arrayHashCode(int[] array) {
 816  0 int result = 1;
 817  0 for (int elt : array) { result = result*31 + ((Integer) elt).hashCode(); }
 818  0 return result;
 819    }
 820   
 821    /**
 822    * Generate a hash code for the given array. The result is calculated as specified by
 823    * {@link java.util.List#hashCode}, invoking the {@code Long#hashCode} method on
 824    * each element. (Note that {@link Arrays#hashCode(long[])} implements the
 825    * same method; this is defined here for compatibility with earlier APIs.)
 826    */
 827  0 public static int arrayHashCode(long[] array) {
 828  0 int result = 1;
 829  0 for (long elt : array) { result = result*31 + ((Long) elt).hashCode(); }
 830  0 return result;
 831    }
 832   
 833    /**
 834    * Generate a hash code for the given array. The result is calculated as specified by
 835    * {@link java.util.List#hashCode}, invoking the {@code Float#hashCode} method on
 836    * each element. (Note that {@link Arrays#hashCode(float[])} implements the
 837    * same method; this is defined here for compatibility with earlier APIs.)
 838    */
 839  0 public static int arrayHashCode(float[] array) {
 840  0 int result = 1;
 841  0 for (float elt : array) { result = result*31 + ((Float) elt).hashCode(); }
 842  0 return result;
 843    }
 844   
 845    /**
 846    * Generate a hash code for the given array. The result is calculated as specified by
 847    * {@link java.util.List#hashCode}, invoking the {@code Double#hashCode} method on
 848    * each element. (Note that {@link Arrays#hashCode(double[])} implements the
 849    * same method; this is defined here for compatibility with earlier APIs.)
 850    */
 851  0 public static int arrayHashCode(double[] array) {
 852  0 int result = 1;
 853  0 for (double elt : array) { result = result*31 + ((Double) elt).hashCode(); }
 854  0 return result;
 855    }
 856   
 857   
 858    /** Defines the representation to be used in array string-generating methods */
 859    public static enum ArrayStringMode {
 860    /** Arrays are printed according to the array {@code toString} method */
 861    CLASS_NAME {
 862  0 protected String prefix() { throw new IllegalArgumentException(); }
 863  0 protected String delimiter() { throw new IllegalArgumentException(); }
 864  0 protected String suffix() { throw new IllegalArgumentException(); }
 865  0 protected ArrayStringMode nestedMode() { throw new IllegalArgumentException(); }
 866    },
 867    /** Arrays are printed with an element type name and a size, like {@code "String[10]"} */
 868    TYPE_AND_SIZE {
 869  0 protected String prefix() { throw new IllegalArgumentException(); }
 870  0 protected String delimiter() { throw new IllegalArgumentException(); }
 871  0 protected String suffix() { throw new IllegalArgumentException(); }
 872  0 protected ArrayStringMode nestedMode() { throw new IllegalArgumentException(); }
 873    },
 874    /**
 875    * Arrays are printed as in {@code Arrays#toString(Object[])}, like
 876    * {@code "[1, 2, 3]"}; nested arrays use {@link #CLASS_NAME}
 877    */
 878    SHALLOW_BRACKETED {
 879  0 protected String prefix() { return "["; }
 880  0 protected String delimiter() { return ", "; }
 881  0 protected String suffix() { return "]"; }
 882  0 protected ArrayStringMode nestedMode() { return CLASS_NAME; }
 883    },
 884    /**
 885    * Arrays are printed as in {@code Arrays#deepToString}, like {@code "[1, 2, 3]"};
 886    * nested arrays also use {@code #DEEP_BRACKETED}
 887    */
 888    DEEP_BRACKETED {
 889  0 protected String prefix() { return "["; }
 890  0 protected String delimiter() { return ", "; }
 891  0 protected String suffix() { return "]"; }
 892  0 protected ArrayStringMode nestedMode() { return DEEP_BRACKETED; }
 893    },
 894    /**
 895    * Arrays are printed like array initializers, using braces (<code>"{ 1, 2, 3 }"</code>);
 896    * nested arrays use {@link #TYPE_AND_SIZE}
 897    */
 898    SHALLOW_BRACED {
 899  0 protected String prefix() { return "{ "; }
 900  0 protected String delimiter() { return ", "; }
 901  0 protected String suffix() { return " }"; }
 902  0 protected ArrayStringMode nestedMode() { return TYPE_AND_SIZE; }
 903    },
 904    /**
 905    * Arrays are printed like array initializers, using braces (<code>"{ 1, 2, 3 }"</code>);
 906    * nested arrays also use {@link #DEEP_BRACED}
 907    */
 908    DEEP_BRACED {
 909  4 protected String prefix() { return "{ "; }
 910  8 protected String delimiter() { return ", "; }
 911  4 protected String suffix() { return " }"; }
 912  8 protected ArrayStringMode nestedMode() { return DEEP_BRACED; }
 913    },
 914    /** Arrays are printed with a single entry on each line; nested arrays use {@link #DEEP_BRACED} */
 915    DEEP_MULTILINE {
 916  0 protected String prefix() { return ""; }
 917  0 protected String delimiter() { return TextUtil.NEWLINE; }
 918  0 protected String suffix() { return ""; }
 919  0 protected ArrayStringMode nestedMode() { return DEEP_BRACED; }
 920    },
 921    /** Arrays are printed with a single entry on each line; nested arrays use {@link #SHALLOW_BRACED} */
 922    SHALLOW_MULTILINE {
 923  0 protected String prefix() { return ""; }
 924  0 protected String delimiter() { return TextUtil.NEWLINE; }
 925  0 protected String suffix() { return ""; }
 926  0 protected ArrayStringMode nestedMode() { return SHALLOW_BRACED; }
 927    };
 928   
 929    protected abstract String prefix();
 930    protected abstract String delimiter();
 931    protected abstract String suffix();
 932    protected abstract ArrayStringMode nestedMode();
 933    }
 934   
 935    }