public void attachClasses(Collection<Class<?>> classes) {
      classes.forEach(this::logClass);

      Map<String, List<ClassInfo>> classesByNames = myClasses.values().stream().collect(Collectors.groupingBy(info -> info.name));
      for (Map.Entry<String, List<ClassInfo>> entry : classesByNames.entrySet()) {
        // we may discover a class that is not in classes and was not yet added via attachClasses
        if (entry.getValue().size() <= 1) {
          entry.getValue().forEach(info -> info.hasSameNamedClasses = false);
          continue;
        }

        // we have same-named class from several class loaders
        // It is only allowed (see ClassListParser::load_class_from_source function)
        // to have one class without source and one class with source.
        //
        // we check if there were classes from the previous call to #attachClasses
        // next we try to leave class without source, or any other class

        ClassInfo classWithSource = null;
        ClassInfo classWithoutSource = null;

        //check decided classes for results
        for (ClassInfo info : entry.getValue()) {
          if (info.hasSameNamedClasses == null) continue;

          if (!Boolean.TRUE.equals(info.hasSameNamedClasses)) {
            if (info.source == null) {
              classWithoutSource = info;
            }
            else {
              classWithSource = info;
            }
          }
        }

        //check undecided classes and update
        for (ClassInfo info : entry.getValue()) {
          if (info.hasSameNamedClasses != null) continue;

          if (classWithoutSource == null && info.source == null) {
            classWithoutSource = info;
            info.hasSameNamedClasses = false;
            continue;
          }

          if (classWithSource == null && info.source != null) {
            classWithSource = info;
            info.hasSameNamedClasses = false;
            continue;
          }

          info.hasSameNamedClasses = true;
        }
      }

      //compute isValid predicate explicitly (it has cache)
      myClasses.values().forEach(this::isValid);

      myClasses.values().stream()
        .sorted(Comparator.comparing(info -> info.name))
        .forEach(this::logLine);
    }
public void attachClasses(Collection<Class<?>> classes)