public void walk(Object root) {
    if (visited.contains(root)) {
      return;
    }
    visited.add(root);
    for (EntryAction onEntryAction : onEntryActions) {
      onEntryAction.onEntry(root);
    }
    Field[] fields = allFields(root.getClass());
    for (Field field : fields) {
      try {
        if (!shouldTraverse(field.getType())) {
          continue;
        }
        Object contents = field.get(root);
        if (contents != null) {
          if (contents.getClass().isArray()) {
            List aryElements = Arrays.asList(contents);
            for (Object aryElement : aryElements) {
              walk(aryElement);
            }
          } else {
            walk(contents);
          }
        }
      } catch (IllegalAccessException e) {
        e.printStackTrace();
      }
    }
    for (ExitAction onExitAction: onExitActions) {
      onExitAction.onExit(root);
    }
  }