class ThriftMetastoreUtil {

    public static Stream<RoleGrant> listApplicableRoles(HivePrincipal principal, Function<HivePrincipal, Set<RoleGrant>> listRoleGrants)
    {
        return Streams.stream(new AbstractIterator<>()
        {
            private final Queue<RoleGrant> output = new ArrayDeque<>();
            private final Set<RoleGrant> seenRoles = new HashSet<>();
            private final Queue<HivePrincipal> queue = new ArrayDeque<>();

            {
                queue.add(principal);
            }

            @Override
            protected RoleGrant computeNext()
            {
                while (output.isEmpty() && !queue.isEmpty()) {
                    Set<RoleGrant> grants = listRoleGrants.apply(queue.remove());
                    for (RoleGrant grant : grants) {
                        if (seenRoles.add(grant)) {
                            output.add(grant);
                            queue.add(new HivePrincipal(ROLE, grant.getRoleName()));
                        }
                    }
                }
                if (!output.isEmpty()) {
                    return output.remove();
                }
                return endOfData();
            }
        });
    }

    private  ThriftMetastoreUtil();

}