public static String diffSortedClassesAndMethods(List<ClassAndMethodDetails> goldRecord,
      List<ClassAndMethods> toDatas) throws IOException {

    StringBuilder newClassesSb = new StringBuilder(10000);
    StringBuilder changedClassesSb = new StringBuilder(10000);
    newClassesSb.append("New or moved classes----------------------------------------\n");
    int newBase = newClassesSb.length();
    changedClassesSb.append("Modified classes--------------------------------------------\n");
    int changedBase = changedClassesSb.length();

    Iterator<ClassAndMethods> it = toDatas.iterator();
    ClassAndMethods newclass = null;

    for (ClassAndMethodDetails gold : goldRecord) {
      if (newclass == null) {
        if (!it.hasNext()) {
          changedClassesSb.append(gold).append(": deleted or moved\n");
          continue;
        }
        newclass = it.next();
      }
      int comparison = -1;
      while (newclass != null
          && (comparison = gold.className.compareTo(newclass.dclass.fullyQualifiedName())) > 0) {
        newClassesSb.append(newclass).append("\n");
        if (it.hasNext()) {
          newclass = it.next();
        } else {
          newclass = null;
        }
      }
      if (comparison == 0) {
        ClassAndMethods nc = newclass;
        newclass = null;
        if (gold.methods.size() != nc.numMethods()) {
          changedClassesSb.append(nc).append(": method count (expected " + gold.methods.size()
              + " but found " + nc.numMethods() + ")\n");
          continue;
        }
        boolean comma = false;
        for (Map.Entry<String, CompiledMethod> entry : nc.methods.entrySet()) {
          CompiledMethod method = entry.getValue();
          String methodName = method.name();
          if (!methodName.equals("toData") && !methodName.equals("fromData")
              && !allowedDataSerializerMethods.contains(methodName)) {
            if (comma) {
              changedClassesSb.append(", and ");
            } else {
              changedClassesSb.append(nc).append(":  ");
              comma = true;
            }
            changedClassesSb.append(methodName)
                .append(" is not a valid method name - doesn't match any Version");
            continue;
          }
          Integer goldCode = gold.methods.get(methodName);
          if (goldCode == null) {
            if (comma) {
              changedClassesSb.append(", and ");
            } else {
              changedClassesSb.append(nc).append(":  ");
              comma = true;
            }
            changedClassesSb.append(methodName).append(" was added");
            continue; // only report one diff per class
          }
          if (goldCode != method.getCode().code.length) {
            if (comma) {
              changedClassesSb.append(", and ");
            } else {
              changedClassesSb.append(nc).append(":  ");
              comma = true;
            }
            changedClassesSb.append(methodName)
                .append(" (len=" + method.getCode().code.length + ",expected=" + goldCode + ")");
            continue;
          }
        }
        for (Map.Entry<String, Integer> entry : gold.methods.entrySet()) {
          if (!nc.methods.containsKey(entry.getKey())) {
            if (comma) {
              changedClassesSb.append(", and ");
            } else {
              changedClassesSb.append(nc).append(":  ");
            }
            changedClassesSb.append(entry.getKey()).append(" is missing");
          }
        }
        if (comma) {
          changedClassesSb.append("\n");
        }
      }
    }
    while (it.hasNext()) {
      newclass = it.next();
      newClassesSb.append(newclass).append(": new class\n");
    }
    String result = "";
    if (newClassesSb.length() > newBase) {
      if (changedClassesSb.length() > changedBase) {
        newClassesSb.append("\n");
        newClassesSb.append(changedClassesSb);
      }
      result = newClassesSb.toString();
    } else if (changedClassesSb.length() > changedBase) {
      result = changedClassesSb.toString();
    }
    return result;
  }
returns a collection of all of the .class files in the given list of files and directories.

@param parentPath the path of the parent directory in which the files exist
@param filenames a list of the files and directories to examine
@param recursive whether to recurse into subdirectories
@return a sorted list of the .class files found
geode-junit/src/main/java/org/apache/geode/codeAnalysis/CompiledClassUtils.java