private TestStatus getStatus(List<SpecReference> references) {
   if (references.isEmpty()) {
     return TestStatus.UNCOVERED;
   }
   for (SpecReference reference : references) {
     if (isImplemented(reference.getGroups())) {
       return TestStatus.COVERED;
     }
   }
   return TestStatus.UNIMPLEMENTED;
 }
  private void calculateUnmatched() {
    unmatched = new ArrayList<SpecReference>();

    for (String sectionId : references.keySet()) {
      for (SpecReference ref : references.get(sectionId)) {
        if (!auditParser.hasAssertion(ref.getSection(), ref.getAssertion())) {
          unmatched.add(ref);
        }
      }
    }
  }
  private List<SpecReference> getCoverageForAssertion(String sectionId, String assertionId) {
    List<SpecReference> refs = new ArrayList<SpecReference>();

    if (references.containsKey(sectionId)) {
      for (SpecReference ref : references.get(sectionId)) {
        if (ref.getAssertion().equals(assertionId)) {
          refs.add(ref);
        }
      }
    }

    return refs;
  }
  public CoverageReport(List<SpecReference> references, AuditParser auditParser, File imageSrcDir) {
    this.references = new HashMap<String, List<SpecReference>>();

    for (SpecReference ref : references) {
      if (!this.references.containsKey(ref.getSection())) {
        this.references.put(ref.getSection(), new ArrayList<SpecReference>());
      }

      this.references.get(ref.getSection()).add(ref);
    }

    this.auditParser = auditParser;
    this.imageSrcDir = imageSrcDir;
    this.properties = new RuntimeProperties();

    try {
      fisheyeBaseUrl = this.properties.getStringValue(FISHEYE_BASE_URL_PROPERTY, null, false);

      if (!fisheyeBaseUrl.endsWith("/")) {
        fisheyeBaseUrl = fisheyeBaseUrl + "/";
      }

      svnBaseUrl = this.properties.getStringValue(SVN_BASE_URL_PROPERTY, null, false);

      if (!svnBaseUrl.endsWith("/")) {
        svnBaseUrl = svnBaseUrl + "/";
      }

      passThreshold = this.properties.getIntValue("pass_threshold", 75, false);
      failThreshold = this.properties.getIntValue("fail_threshold", 50, false);

      String unimplemented =
          this.properties.getStringValue("unimplemented_test_groups", null, false);
      if (unimplemented != null) {
        String[] parts = unimplemented.split(",");
        unimplementedTestGroups = new HashSet<String>();
        for (String part : parts) {
          if (!"".equals(part.trim())) {
            unimplementedTestGroups.add(part.trim());
          }
        }
      }
    } catch (Exception ex) {
      // swallow
    }
  }
  private void writeUnmatched(OutputStream out) throws IOException {
    if (unmatched.isEmpty()) return;

    StringBuilder sb = new StringBuilder();

    sb.append("<h3 id=\"unmatched\">Unmatched tests</h3>\n");
    sb.append(
        String.format(
            "<p>The following %d tests do not match any known assertions:</p>", unmatched.size()));

    sb.append("<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\">\n");
    sb.append(
        "  <tr><th>Section</th><th>Assertion</th><th>Test Class</th><th>Test Method</th></tr>\n");

    for (SpecReference ref : unmatched) {
      sb.append("<tr>");

      sb.append("<td>");
      sb.append(ref.getSection());
      sb.append("</td>");

      sb.append("<td>");
      sb.append(ref.getAssertion());
      sb.append("</td>");

      sb.append("<td>");
      sb.append("<div class=\"packageName\">");
      sb.append(ref.getPackageName());
      sb.append("</div>");
      sb.append(ref.getClassName());
      sb.append("</td>");

      sb.append("<td>");
      sb.append(ref.getMethodName());
      sb.append("()");
      sb.append("</td>");

      sb.append("</tr>");
    }

    sb.append("</table>");

    out.write(sb.toString().getBytes());
  }
  private void writeCoverage(OutputStream out) throws IOException {

    out.write("<h3 id=\"coverageDetail\">Coverage Detail</h3>\n".getBytes());

    StringBuilder key = new StringBuilder();
    key.append("<table>");
    key.append("<tr><th style=\"background-color:#dddddd\">Colour Key</th></tr>");
    key.append(
        "<tr><td style=\"background-color:"
            + COLOUR_SHADE_GREEN
            + ";text-align:center\">Assertion is covered</td></tr>");
    key.append(
        "<tr><td style=\"background-color:"
            + COLOUR_SHADE_RED
            + ";text-align:center\">Assertion is not covered</td></tr>");
    key.append(
        "<tr><td style=\"background-color:"
            + COLOUR_SHADE_ORANGE
            + ";text-align:center\">Assertion test is unimplemented</td></tr>");
    key.append(
        "<tr><td style=\"background-color:"
            + COLOUR_SHADE_BLUE
            + ";text-align:center\">Assertion is untestable</td></tr>");
    key.append("</table>");
    out.write(key.toString().getBytes());

    for (String sectionId : auditParser.getSectionIds()) {

      List<AuditAssertion> sectionAssertions = auditParser.getAssertionsForSection(sectionId);

      if (sectionAssertions != null && !sectionAssertions.isEmpty()) {
        StringBuilder sb = new StringBuilder();

        out.write(
            ("<h4 class=\"sectionHeader\" id=\""
                    + sectionId
                    + "\">Section "
                    + sectionId
                    + " - "
                    + escape(auditParser.getSectionTitle(sectionId))
                    + "</h4>\n")
                .getBytes());

        for (AuditAssertion assertion : sectionAssertions) {
          List<SpecReference> coverage = getCoverageForAssertion(sectionId, assertion.getId());
          TestStatus status = getStatus(coverage);

          String divClass = null;

          if (assertion.isTestable()) {
            if (status.equals(TestStatus.UNCOVERED)) {
              divClass = "fail";
            } else if (status.equals(TestStatus.UNIMPLEMENTED)) {
              divClass = "skip";
            } else {
              divClass = "pass";
            }
          } else {
            divClass = "untestable";
          }

          sb.append("  <div class=\"" + divClass + "\">\n");

          if (assertion.isImplied()) {
            sb.append(
                "<span class=\"implied\">The following assertion is not made explicitly by the spec, however it is implied</span>");
          }

          sb.append("    <span class=\"code\">");
          sb.append(assertion.getId());
          sb.append(")");

          if (!Strings.isEmpty(assertion.getNote())) {
            sb.append(
                "<img title=\""
                    + assertion.getNote()
                    + "\" src=\"images/blank.png\" class=\"stickynote\"/>");
            // sb.append("<a title=\"" + assertion.getNote() + "\"><img title=\"" +
            // assertion.getNote() + "\" src=\"images/blank.png\" class=\"stickynote\"/></a>");
          }

          sb.append("</span>\n");

          sb.append("    <div class=\"results\">");

          sb.append("<p class=\"description\">");
          String imageFilename = sectionId + "." + assertion.getId() + ".png";
          File imageFile = new File(imageSrcDir, imageFilename);

          if (imageFile.exists()) {
            sb.append("<img src=\"images/" + imageFile.getName() + "\" class=\"embeddedImage\"/>");
            copyFile(imageFile, new File(imageTargetDir, imageFilename));
          }

          String assertionText =
              parseStrikethrough(parseBold(parseLiteral(escape(assertion.getText()))));
          sb.append(assertionText);
          sb.append("</p>\n");

          if (assertion.isTestable()) {
            sb.append("    <div class=\"coverage\">\n");
            sb.append("      <p class=\"coverageHeader\">Coverage</p>\n");

            String currentPackageName = null;

            if (status.equals(TestStatus.UNCOVERED)) {
              sb.append("        <p class=\"noCoverage\">No tests exist for this assertion</p>\n");
            } else {
              for (SpecReference ref : coverage) {
                if (!ref.getPackageName().equals(currentPackageName)) {
                  currentPackageName = ref.getPackageName();
                  sb.append("        <div class=\"packageName\">");
                  sb.append(currentPackageName);
                  sb.append("        </div>\n");
                }

                sb.append("        <div class=\"coverageMethod\">");
                sb.append(ref.getClassName());
                sb.append(".");
                sb.append(ref.getMethodName());
                sb.append("()");

                if (fisheyeBaseUrl != null) {
                  sb.append("<a class=\"external\" target=\"_blank\" href=\"");
                  sb.append(fisheyeBaseUrl);
                  sb.append(currentPackageName.replace('.', '/'));
                  sb.append("/");
                  sb.append(ref.getClassName());
                  sb.append(".java");
                  sb.append("\">fisheye</a>");
                }

                if (svnBaseUrl != null) {
                  if (fisheyeBaseUrl != null) {
                    sb.append("|");
                  }

                  sb.append("<a class=\"external\" target=\"_blank\" href=\"");
                  sb.append(svnBaseUrl);
                  sb.append(currentPackageName.replace('.', '/'));
                  sb.append("/");
                  sb.append(ref.getClassName());
                  sb.append(".java");
                  sb.append("\">svn</a>");
                }

                sb.append("</div>\n");
              }
            }

            sb.append("    </div>\n");
          } else if (!coverage.isEmpty()) {
            sb.append("<b>A test exists for this untestable assertion!</b>");
          }

          sb.append("</div></div>");
        }

        out.write(sb.toString().getBytes());
      } else {
        // We still want to be able to jump to this section by clicking on the links
        // in the chapter and section summaries
        out.write(
            ("<div style=\"visibility:hidden\" id=\"" + sectionId + "\"></div>\n").getBytes());
      }
    }
  }