/**
   * Clone given method into this class with given name. Update necessary state of prior analyses.
   */
  private SootMethod cloneMethod(SootMethod ancestorM, String cloneName) {
    // check if we are cloning a method multiple times
    if (clonedToOriginal.containsValue(ancestorM)) {
      logger.error("Cloning method twice: {}", ancestorM);
      droidsafe.main.Main.exit(1);
    }

    SootMethod newMeth =
        new SootMethod(
            cloneName,
            ancestorM.getParameterTypes(),
            ancestorM.getReturnType(),
            ancestorM.getModifiers(),
            ancestorM.getExceptions());

    // System.out.printf("\tAdding method %s.\n", ancestorM);
    // register method
    methods.addMethod(newMeth);
    clazz.addMethod(newMeth);

    clonedToOriginal.put(newMeth, ancestorM);

    API.v().cloneMethodClassifications(ancestorM, newMeth);

    // clone body
    Body newBody = (Body) ancestorM.retrieveActiveBody().clone();
    newMeth.setActiveBody(newBody);

    JSAStrings.v().updateJSAResults(ancestorM.retrieveActiveBody(), newBody);

    return newMeth;
  }
  private static String reachableSinksSources() {
    StringBuffer buf = new StringBuffer();
    Set<Stmt> sinks = new HashSet<Stmt>();
    Set<Stmt> sources = new HashSet<Stmt>();

    for (SootMethod method : CollaspedCallGraph.v().getAllMethods()) {
      for (CallToTarget apiCall : CollaspedCallGraph.v().getAPICallTargets(method)) {
        if (API.v().hasSourceInfoKind(apiCall.getTarget())
            && InfoKind.callsSensitiveSource(apiCall.getStmt())) {
          sources.add(apiCall.getStmt());
        }

        if (API.v().hasSinkInfoKind(apiCall.getTarget())
            && InfoKind.callsSensitiveSink(apiCall.getStmt())) {
          sinks.add(apiCall.getStmt());
        }
      }
    }

    buf.append("Total reachable sink call statments: " + sinks.size() + "\n");
    buf.append("Total reachable source call statements: " + sources.size() + "\n");

    return buf.toString();
  }
  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());
      }
    }
  }
  private void addGUIClasses(StringBuffer buf) {
    if (buf.length() > 0 && ',' != buf.charAt(buf.length() - 1)) buf.append(',');
    int i = 0;
    int j = 0;
    for (SootClass clz : Scene.v().getClasses()) {
      if (clz.isInterface()) continue;

      j++;

      if (API.v().isGUIClass(clz)) {
        buf.append(clz + ",");
        i++;
        logger.info("Adding class to limit heap context list of spark: {}", clz);
      }
    }
    System.out.println("GUI no context " + i);
    System.out.println("Total classes: " + j);
  }
 /** Calculate the score for each entry in the call chain * */
 public void calculate_scores() {
   score = 0;
   if (contents.length == 0) {
     API api = API.v();
     Set<InfoKind> source = api.getSourceInfoKinds(method);
     Set<InfoKind> sink = api.getSinkInfoKinds(method);
     if (is_system(method)) {
       if (api.isSafeMethod(method)) score = 0;
       else if (api.isSpecMethod(method)) score = 5;
       else if (api.isBannedMethod(method)) score = 6;
       if (!source.isEmpty()) score += 1;
       else if (!sink.isEmpty()) score += 2;
     }
     return;
   }
   for (SourceCallChainInfo cci : contents) {
     cci.calculate_scores();
     calls += cci.calls;
     syscalls += cci.syscalls;
     if (cci.score > score) score = cci.score;
   }
 }