void dump(
      Collection<BugPatternInstance> patterns, Writer w, Target target, Set<String> enabledChecks)
      throws IOException {
    // (Default, Severity) -> [Pattern...]
    SortedSetMultimap<IndexEntry, MiniDescription> sorted =
        TreeMultimap.create(
            comparing(IndexEntry::onByDefault, trueFirst()).thenComparing(IndexEntry::severity),
            Comparator.comparing(MiniDescription::name));
    for (BugPatternInstance pattern : patterns) {
      sorted.put(
          IndexEntry.create(enabledChecks.contains(pattern.name), pattern.severity),
          MiniDescription.create(pattern));
    }

    Map<String, Object> templateData = new HashMap<>();

    ImmutableList<Map<String, Object>> bugpatternData =
        Multimaps.asMap(sorted).entrySet().stream()
            .map(
                e ->
                    ImmutableMap.of(
                        "category", e.getKey().asCategoryHeader(), "checks", e.getValue()))
            .collect(toImmutableList());

    templateData.put("bugpatterns", bugpatternData);

    if (target == Target.EXTERNAL) {
      ImmutableMap<String, String> frontmatterData =
          ImmutableMap.<String, String>builder()
              .put("title", "Bug Patterns")
              .put("layout", "bugpatterns")
              .buildOrThrow();
      DumperOptions options = new DumperOptions();
      options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
      Yaml yaml = new Yaml(options);
      Writer yamlWriter = new StringWriter();
      yamlWriter.write("---\n");
      yaml.dump(frontmatterData, yamlWriter);
      yamlWriter.write("---\n");
      templateData.put("frontmatter", yamlWriter.toString());

      MustacheFactory mf = new DefaultMustacheFactory();
      Mustache mustache =
          mf.compile("com/google/errorprone/resources/bugpatterns_external.mustache");
      mustache.execute(w, templateData);
    } else {
      MustacheFactory mf = new DefaultMustacheFactory();
      Mustache mustache =
          mf.compile("com/google/errorprone/resources/bugpatterns_internal.mustache");
      mustache.execute(w, templateData);
    }
  }
@Test
  public void dumpExternal() throws Exception {
    StringWriter writer = new StringWriter();

    BugPatternInstance pattern1 = new BugPatternInstance();
    pattern1.severity = SeverityLevel.ERROR;
    pattern1.name = "BugPatternA";
    pattern1.summary = "Here's the \"interesting\" summary";

    BugPatternInstance pattern2 = new BugPatternInstance();
    pattern2.severity = SeverityLevel.ERROR;
    pattern2.name = "BugPatternB";
    pattern2.summary = "{summary2}";

    BugPatternInstance pattern3 = new BugPatternInstance();
    pattern3.severity = SeverityLevel.ERROR;
    pattern3.name = "BugPatternC";
    pattern3.summary = "mature";

    new BugPatternIndexWriter()
        .dump(
            Arrays.asList(pattern3, pattern2, pattern1),
            writer,
            Target.EXTERNAL,
            ImmutableSet.of("BugPatternC"));
    assertThat(writer.toString())
        .isEqualTo(
            "---\n"
                + "title: Bug Patterns\n"
                + "layout: bugpatterns\n"
                + "---\n\n\n"
                + "# Bug patterns\n"
                + "\n"
                + "This list is auto-generated from our sources.\n"
                + "\n"
                + "Patterns which are marked __Experimental__ will not be evaluated against your\n"
                + "code, unless you specifically configure Error Prone. The default checks are\n"
                + "marked __On by default__, and each release promotes some experimental checks\n"
                + "after we've vetted them against Google's codebase.\n"
                + "\n"
                + "## On by default : ERROR\n"
                + "\n"
                + "__[BugPatternC](bugpattern/BugPatternC)__<br>\n"
                + "mature\n"
                + "\n"
                + "## Experimental : ERROR\n"
                + "\n"
                + "__[BugPatternA](bugpattern/BugPatternA)__<br>\n"
                + "Here&#39;s the &quot;interesting&quot; summary\n"
                + "\n"
                + "__[BugPatternB](bugpattern/BugPatternB)__<br>\n"
                + "{summary2}\n"
                + "\n");
  }