@Override
  public boolean processLine(String line) throws IOException {
    BugPatternInstance pattern = new Gson().fromJson(line, BugPatternInstance.class);
    pattern.severity = severityRemapper.apply(pattern);
    result.add(pattern);

    // replace spaces in filename with underscores
    Path checkPath = Paths.get(pattern.name.replace(' ', '_') + ".md");

    try (Writer writer = Files.newBufferedWriter(outputDir.resolve(checkPath), UTF_8)) {

      // load side-car explanation file, if it exists
      Path sidecarExplanation = explanationDir.resolve(checkPath);
      if (Files.exists(sidecarExplanation)) {
        if (!pattern.explanation.isEmpty()) {
          throw new AssertionError(
              String.format(
                  "%s specifies an explanation via @BugPattern and side-car", pattern.name));
        }
        pattern.explanation = new String(Files.readAllBytes(sidecarExplanation), UTF_8).trim();
      }

      // Construct an appropriate page for this {@code BugPattern}. Include altNames if
      // there are any, and explain the correct way to suppress.

      ImmutableMap.Builder<String, Object> templateData =
          ImmutableMap.<String, Object>builder()
              .put("tags", Joiner.on(", ").join(pattern.tags))
              .put("severity", pattern.severity)
              .put("name", pattern.name)
              .put("className", pattern.className)
              .put("summary", pattern.summary.trim())
              .put("altNames", Joiner.on(", ").join(pattern.altNames))
              .put("explanation", pattern.explanation.trim());

      if (baseUrl != null) {
        templateData.put("baseUrl", baseUrl);
      }

      if (generateFrontMatter) {
        ImmutableMap<String, String> frontmatterData =
            ImmutableMap.<String, String>builder()
                .put("title", pattern.name)
                .put("summary", pattern.summary)
                .put("layout", "bugpattern")
                .put("tags", Joiner.on(", ").join(pattern.tags))
                .put("severity", pattern.severity.toString())
                .buildOrThrow();
        DumperOptions options = new DumperOptions();
        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
        Yaml yaml =
            new Yaml(
                new SafeConstructor(new LoaderOptions()),
                new Representer(new DumperOptions()),
                options);
        Writer yamlWriter = new StringWriter();
        yamlWriter.write("---\n");
        yaml.dump(frontmatterData, yamlWriter);
        yamlWriter.write("---\n");
        templateData.put("frontmatter", yamlWriter.toString());
      }

      if (pattern.documentSuppression) {
        String suppressionString;
        if (pattern.suppressionAnnotations.length == 0) {
          suppressionString = "This check may not be suppressed.";
        } else {
          suppressionString =
              pattern.suppressionAnnotations.length == 1
                  ? "Suppress false positives by adding the suppression annotation %s to the "
                      + "enclosing element."
                  : "Suppress false positives by adding one of these suppression annotations to "
                      + "the enclosing element: %s";
          suppressionString =
              String.format(
                  suppressionString,
                  Arrays.stream(pattern.suppressionAnnotations)
                      .map((String anno) -> standardizeAnnotation(anno, pattern.name))
                      .collect(Collectors.joining(", ")));
        }
        templateData.put("suppression", suppressionString);
      }

      MustacheFactory mf = new DefaultMustacheFactory();
      Mustache mustache = mf.compile("com/google/errorprone/resources/bugpattern.mustache");
      mustache.execute(writer, templateData.buildOrThrow());
    }
    return true;
  }
@Test
  public void regressionTest_sidecar() throws Exception {
    BugPatternFileGenerator generator =
        new BugPatternFileGenerator(
            wikiDir, explanationDirBase, false, null, input -> input.severity);
    Files.write(
        explanationDirBase.resolve("DeadException.md"),
        Arrays.asList(
            "The exception is created with new, but is not thrown, and the reference is lost."),
        UTF_8);
    generator.processLine(BUGPATTERN_LINE_SIDECAR);
    String expected =
        CharStreams.toString(
            new InputStreamReader(
                getClass().getResourceAsStream("testdata/DeadException_nofrontmatter_gfm.md"),
                UTF_8));
    String actual = new String(Files.readAllBytes(wikiDir.resolve("DeadException.md")), UTF_8);
    assertThat(actual.trim()).isEqualTo(expected.trim());
  }