private final void setTableValue(
     GATKReportTable table, final String rgID, final String key, final Object value) {
   table.set(rgID, key, value == null ? "NA" : value);
 }
  @Override
  public void onTraversalDone(Integer sum) {
    final GATKReport report = new GATKReport();
    report.addTable(TABLE_NAME, "Table of read group properties", 12);
    GATKReportTable table = report.getTable(TABLE_NAME);
    DateFormat dateFormatter = DateFormat.getDateInstance(DateFormat.SHORT);

    table.addColumn("readgroup");
    // * Emits a GATKReport containing read group, sample, library, platform, center, median insert
    // size and
    // * median read length for each read group in every BAM file.
    table.addColumn("sample", "%s");
    table.addColumn("library", "%s");
    table.addColumn("platform", "%s");
    table.addColumn("center", "%s");
    table.addColumn("date", "%s");
    table.addColumn("has.any.reads");
    table.addColumn("is.paired.end");
    table.addColumn("n.reads.analyzed", "%d");
    table.addColumn("simple.read.type", "%s");
    table.addColumn("median.read.length");
    table.addColumn("median.insert.size");

    for (final SAMReadGroupRecord rg : getToolkit().getSAMFileHeader().getReadGroups()) {
      final String rgID = rg.getId();
      table.addRowID(rgID, true);
      PerReadGroupInfo info = readGroupInfo.get(rgID);

      // we are paired if > 25% of reads are paired
      final boolean isPaired = info.nReadsPaired / (1.0 * (info.nReadsSeen + 1)) > 0.25;
      final boolean hasAnyReads = info.nReadsSeen > 0;
      final int readLength = info.readLength.getMedian(0);

      setTableValue(table, rgID, "sample", rg.getSample());
      setTableValue(table, rgID, "library", rg.getLibrary());
      setTableValue(table, rgID, "platform", rg.getPlatform());
      setTableValue(table, rgID, "center", rg.getSequencingCenter());
      try {
        setTableValue(
            table,
            rgID,
            "date",
            rg.getRunDate() != null ? dateFormatter.format(rg.getRunDate()) : "NA");
      } catch (NullPointerException e) {
        // TODO: remove me when bug in Picard is fixed that causes NPE when date isn't present
        setTableValue(table, rgID, "date", "NA");
      }
      setTableValue(table, rgID, "has.any.reads", hasAnyReads);
      setTableValue(table, rgID, "is.paired.end", isPaired);
      setTableValue(table, rgID, "n.reads.analyzed", info.nReadsSeen);
      setTableValue(
          table,
          rgID,
          "simple.read.type",
          hasAnyReads ? String.format("%dx%d", isPaired ? 2 : 1, readLength) : "NA");
      setTableValue(table, rgID, "median.read.length", hasAnyReads ? readLength : "NA");
      setTableValue(
          table,
          rgID,
          "median.insert.size",
          hasAnyReads && isPaired ? info.insertSize.getMedian(0) : "NA");
    }

    report.print(out);
  }