/** merge multiple call chains from different contexts * */
 public void merge(SourceCallChainInfo other) {
   if (method != other.method)
     throw new RuntimeException(
         "methods don't match: "
             + method
             + ", "
             + other.method
             + " "
             + method.equals(other.method)
             + " "
             + method.hashCode()
             + " "
             + other.method.hashCode());
   if (contents.length == 0) {
     contents = other.contents;
     return;
   } else if (other.contents.length == 0) {
     return;
   }
   List<SourceCallChainInfo> ccis = new ArrayList<SourceCallChainInfo>(Arrays.asList(contents));
   Map<SootMethod, SourceCallChainInfo> minfo = new HashMap<SootMethod, SourceCallChainInfo>();
   for (SourceCallChainInfo cci : contents) minfo.put(cci.method, cci);
   for (SourceCallChainInfo other_cci : other.contents) {
     SourceCallChainInfo cci = minfo.get(other_cci.method);
     if (cci == null) ccis.add(other_cci);
     else cci.merge(other_cci);
   }
   if (contents.length == ccis.size())
     logger.info("merge: {} old/new size = {}", method, ccis.size());
   else logger.info("merge: {} orig {} elems, new {} elems", method, contents.length, ccis.size());
   contents = ccis.toArray(new SourceCallChainInfo[0]);
 }
 /** merge any duplicate method calls * */
 public void merge_contents() {
   if (contents.length == 0) return;
   Arrays.sort(contents);
   List<SourceCallChainInfo> unique_calls = new ArrayList<SourceCallChainInfo>();
   unique_calls.add(contents[0]);
   for (int ii = 1; ii < contents.length; ii++) {
     SourceCallChainInfo top = unique_calls.get(unique_calls.size() - 1);
     if (contents[ii].method == top.method) top.merge(contents[ii]);
     else unique_calls.add(contents[ii]);
   }
   logger.info(
       "merge_contents {}: old {} elems, new {} elems",
       method,
       contents.length,
       unique_calls.size());
   contents = unique_calls.toArray(new SourceCallChainInfo[0]);
 }