code: static String deepToString(final Object o) {
          if (o == null) {
              return null;
          }
          if (o instanceof String) {
              return (String) o;
          }
          final StringBuilder str = new StringBuilder();
          final Set<String> dejaVu = new HashSet<>(); // that's actually a neat name ;)
          recursiveDeepToString(o, str, dejaVu);
          return str.toString();
      }
docs: This method performs a deep toString of the given Object.
  Primitive arrays are converted using their respective Arrays.toString methods while
  special handling is implemented for "container types", i.e. Object[], Map and Collection because those could
  contain themselves.
  <p>
  It should be noted that neither AbstractMap.toString() nor AbstractCollection.toString() implement such a
  behavior. They only check if the container is directly contained in itself, but not if a contained container
  contains the original one. Because of that, Arrays.toString(Object[]) isn't safe either.
  Confusing? Just read the last paragraph again and check the respective toString() implementation.
  </p>
  <p>
  This means, in effect, that logging would produce a usable output even if an ordinary System.out.println(o)
  would produce a relatively hard-to-debug StackOverflowError.
  </p>
  @param o The object.
  @return The String representation.