public String next() throws StandardException {
    if (initial) {
      // Optimization so we don't compute the closure for the current
      // role if unnecessary (when next is only called once).
      initial = false;
      seenSoFar.put(root, null);

      return root;

    } else if (graph == null) {
      // We get here the second time next is called.
      graph = dd.getRoleGrantGraph(tc, inverse);
      List outArcs = (List) graph.get(root);
      if (outArcs != null) {
        currNodeIter = outArcs.iterator();
      }
    }

    RoleGrantDescriptor result = null;

    while (result == null) {
      while (currNodeIter.hasNext()) {
        RoleGrantDescriptor r = (RoleGrantDescriptor) currNodeIter.next();

        if (seenSoFar.containsKey(inverse ? r.getRoleName() : r.getGrantee())) {
          continue;
        } else {
          lifo.add(r);
          result = r;
          break;
        }
      }

      if (result == null) {
        // not more candidates located outgoing from the
        // latest found node, pick another and continue
        RoleGrantDescriptor newNode = null;

        currNodeIter = null;

        while (lifo.size() > 0 && currNodeIter == null) {

          newNode = (RoleGrantDescriptor) lifo.remove(lifo.size() - 1);

          // In the example (see interface doc), the
          // iterator of outgoing arcs for f (grant inverse)
          // would contain {e,c,d}.
          List outArcs = (List) graph.get(inverse ? newNode.getRoleName() : newNode.getGrantee());

          if (outArcs != null) {
            currNodeIter = outArcs.iterator();
          } // else: leaf node, pop next candidate, if any
        }

        if (currNodeIter == null) {
          // candidate stack is empty, done
          currNodeIter = null;
          break;
        }
      }
    }

    if (result != null) {
      String role = inverse ? result.getRoleName() : result.getGrantee();
      seenSoFar.put(role, null);
      return role;
    } else {
      return null;
    }
  }