private void dumpTextGraph(SootMethod caller, PrintStream printStream, int level) {

    String indent = indentString(level);
    caller.getTags();
    printStream.printf("%s %s\n", indent, caller.toString());
    Iterator<Edge> iterator = callGraph.edgesOutOf(caller);
    callgraphSet.add(caller);

    // boolean appClass = caller.getDeclaringClass().isApplicationClass();
    boolean systemApi = API.v().isSystemMethod(caller);

    /*
      printStream.printf("%s Declaring method %s: app %s\n", indent,
      caller.toString(), systemApi? "False": "True");
    */

    String subindent = indentString(level + 1);
    Set<Object> calleeSet = new HashSet<Object>();

    while (iterator != null && iterator.hasNext()) {
      Edge edge = iterator.next();
      if (!systemApi) {
        List<Stmt> invokeStmtList = SootUtils.getInvokeStatements(caller, edge.tgt());
        for (Stmt stmt : invokeStmtList) {
          if (calleeSet.contains(stmt)) continue;
          printStream.printf("%s #[%s] ", subindent, stmt);
          SourceLocationTag tag = SootUtils.getSourceLocation(stmt);
          if (tag != null) {
            printStream.printf(": %s", tag.toString());
          }
          printStream.printf("\n");
          calleeSet.add(stmt.toString());
        }
      }

      if (!callgraphSet.contains(edge.tgt())) {
        dumpTextGraph(edge.tgt(), printStream, level + 1);
      } else {
        // already in the call graph, just print it out
        if (calleeSet.contains(edge.tgt())) continue;
        printStream.printf("%s %s\n", subindent, edge.tgt().toString());
        calleeSet.add(edge.tgt());
      }
    }
  }
  /** Dumps out the call chain in json format * */
  public void dump_json(PrintStream fp, String indent) {
    fp.printf("%s{ %s,\n", indent, json_field("type", type));
    fp.printf("%s  %s,\n", indent, json_field("link", link));
    String sig = method.getSignature();
    fp.printf("%s  %s,\n", indent, json_field("signature", sig));
    if (stmt != null) {
      SootMethodRef invoke = stmt.getInvokeExpr().getMethodRef();
      String invokeSig;
      try {
        SootMethod concrete = SootUtils.resolve(stmt.getInvokeExpr().getMethodRef());
        invokeSig = concrete.getSignature();
      } catch (CannotFindMethodException e1) {
        logger.debug("Cannot find concrete method for {} in SourceCallChainInfo.dump_json()", stmt);
        invokeSig = invoke.getSignature();
      }
      if (!invokeSig.equals(sig)) {
        fp.printf("%s  %s,\n", indent, json_field("source-signature", invokeSig));
      }
    }
    SourceLocationTag slt =
        (stmt == null) ? SootUtils.getMethodLocation(method) : getSourceLocation(stmt);
    if (slt != null) {
      fp.printf("%s  %s", indent, json_field("src-loc"));
      fp.printf(
          "{ %s, %s},\n", json_field("class", slt.getClz()), json_field("line", slt.getLine()));
    }
    fp.printf("%s  %s,\n", indent, json_field("syscalls", syscalls));
    fp.printf("%s  %s,\n", indent, json_field("calls", calls));

    if ((contents != null) && (contents.length > 0)) {
      fp.printf("%s  %s,\n", indent, json_field("score", score));
      fp.printf("%s  %s [\n", indent, json_field("contents"));
      String delim = "";
      for (SourceCallChainInfo cci : contents) {
        fp.print(delim);
        delim = ",\n";
        cci.dump_json(fp, indent + "  ");
      }
      fp.printf("\n%s]}", indent);
    } else {
      fp.printf("%s  %s\n", indent, json_field("score", score));
      fp.printf("%s}", indent);
    }
  }