/**
  * Assert that the number of errors that have been gathered matches the number of severities that
  * are given and that there are the same number of errors and warnings as specified by the
  * argument. The order in which the errors were gathered is ignored.
  *
  * @param expectedSeverities the severities of the errors that should have been gathered
  * @throws AssertionFailedError if a different number of errors have been gathered than were
  *     expected
  */
 public void assertErrors(ErrorSeverity... expectedSeverities) {
   int expectedErrorCount = 0;
   int expectedWarningCount = 0;
   for (ErrorSeverity severity : expectedSeverities) {
     if (severity == ErrorSeverity.ERROR) {
       expectedErrorCount++;
     } else {
       expectedWarningCount++;
     }
   }
   int actualErrorCount = 0;
   int actualWarningCount = 0;
   for (AnalysisError error : errors) {
     if (error.getErrorCode().getErrorSeverity() == ErrorSeverity.ERROR) {
       actualErrorCount++;
     } else {
       actualWarningCount++;
     }
   }
   if (expectedErrorCount != actualErrorCount || expectedWarningCount != actualWarningCount) {
     Assert.fail(
         "Expected "
             + expectedErrorCount
             + " errors and "
             + expectedWarningCount
             + " warnings, found "
             + actualErrorCount
             + " errors and "
             + actualWarningCount
             + " warnings");
   }
 }
 /**
  * Return {@code true} if an error with the given error code has been gathered.
  *
  * @param errorCode the error code being searched for
  * @return {@code true} if an error with the given error code has been gathered
  */
 public boolean hasError(ErrorCode errorCode) {
   for (AnalysisError error : errors) {
     if (error.getErrorCode() == errorCode) {
       return true;
     }
   }
   return false;
 }
 @Override
 public void onError(AnalysisError error) {
   if (rawSource != null) {
     int left = error.getOffset();
     int right = left + error.getLength() - 1;
     markedSource =
         rawSource.substring(0, left)
             + "^"
             + rawSource.substring(left, right)
             + "^"
             + rawSource.substring(right);
   }
   errors.add(error);
 }
 /**
  * Assert that the number of errors that have been gathered matches the number of errors that are
  * given and that they have the expected error codes. The order in which the errors were gathered
  * is ignored.
  *
  * @param expectedErrorCodes the error codes of the errors that should have been gathered
  * @throws AssertionFailedError if a different number of errors have been gathered than were
  *     expected
  */
 public void assertErrors(ErrorCode... expectedErrorCodes) {
   StringBuilder builder = new StringBuilder();
   //
   // Compute the expected number of each type of error.
   //
   HashMap<ErrorCode, Integer> expectedCounts = new HashMap<ErrorCode, Integer>();
   for (ErrorCode code : expectedErrorCodes) {
     Integer count = expectedCounts.get(code);
     if (count == null) {
       count = Integer.valueOf(1);
     } else {
       count = Integer.valueOf(count.intValue() + 1);
     }
     expectedCounts.put(code, count);
   }
   //
   // Compute the actual number of each type of error.
   //
   HashMap<ErrorCode, ArrayList<AnalysisError>> errorsByCode =
       new HashMap<ErrorCode, ArrayList<AnalysisError>>();
   for (AnalysisError error : errors) {
     ErrorCode code = error.getErrorCode();
     ArrayList<AnalysisError> list = errorsByCode.get(code);
     if (list == null) {
       list = new ArrayList<AnalysisError>();
       errorsByCode.put(code, list);
     }
     list.add(error);
   }
   //
   // Compare the expected and actual number of each type of error.
   //
   for (Map.Entry<ErrorCode, Integer> entry : expectedCounts.entrySet()) {
     ErrorCode code = entry.getKey();
     int expectedCount = entry.getValue().intValue();
     int actualCount;
     ArrayList<AnalysisError> list = errorsByCode.remove(code);
     if (list == null) {
       actualCount = 0;
     } else {
       actualCount = list.size();
     }
     if (actualCount != expectedCount) {
       if (builder.length() == 0) {
         builder.append("Expected ");
       } else {
         builder.append("; ");
       }
       builder.append(expectedCount);
       builder.append(" errors of type ");
       builder.append(code);
       builder.append(", found ");
       builder.append(actualCount);
     }
   }
   //
   // Check that there are no more errors in the actual-errors map, otherwise, record message.
   //
   for (Map.Entry<ErrorCode, ArrayList<AnalysisError>> entry : errorsByCode.entrySet()) {
     ErrorCode code = entry.getKey();
     ArrayList<AnalysisError> actualErrors = entry.getValue();
     int actualCount = actualErrors.size();
     if (builder.length() == 0) {
       builder.append("Expected ");
     } else {
       builder.append("; ");
     }
     builder.append("0 errors of type ");
     builder.append(code);
     builder.append(", found ");
     builder.append(actualCount);
     builder.append(" (");
     for (int i = 0; i < actualErrors.size(); i++) {
       AnalysisError error = actualErrors.get(i);
       if (i > 0) {
         builder.append(", ");
       }
       builder.append(error.getOffset());
     }
     builder.append(")");
   }
   if (builder.length() > 0) {
     Assert.fail(builder.toString());
   }
 }
 /**
  * Assert that the number of errors that have been gathered matches the number of errors that are
  * given and that they have the expected error codes. The order in which the errors were gathered
  * is ignored.
  *
  * @param errorCodes the errors that should have been gathered
  * @throws AssertionFailedError with
  */
 private void fail(AnalysisError[] expectedErrors) {
   PrintStringWriter writer = new PrintStringWriter();
   writer.print("Expected ");
   writer.print(expectedErrors.length);
   writer.print(" errors:");
   for (AnalysisError error : expectedErrors) {
     Source source = error.getSource();
     File file = source == null ? null : source.getFile();
     LineInfo lineInfo = lineInfoMap.get(source);
     writer.println();
     if (lineInfo == null) {
       int offset = error.getOffset();
       writer.printf(
           "  %s %s (%d..%d)",
           file == null ? "" : file.getName(),
           error.getErrorCode(),
           offset,
           offset + error.getLength());
     } else {
       LineInfo.Location location = lineInfo.getLocation(error.getOffset());
       writer.printf(
           "  %s %s (%d, %d/%d)",
           source == null ? "" : source.getFile().getName(),
           error.getErrorCode(),
           location.getLineNumber(),
           location.getColumnNumber(),
           error.getLength());
     }
   }
   writer.println();
   writer.print("found ");
   writer.print(errors.size());
   writer.print(" errors:");
   for (AnalysisError error : errors) {
     Source source = error.getSource();
     File file = source == null ? null : source.getFile();
     LineInfo lineInfo = lineInfoMap.get(source);
     writer.println();
     if (lineInfo == null) {
       int offset = error.getOffset();
       writer.printf(
           "  %s %s (%d..%d): %s",
           file == null ? "" : file.getName(),
           error.getErrorCode(),
           offset,
           offset + error.getLength(),
           error.getMessage());
     } else {
       LineInfo.Location location = lineInfo.getLocation(error.getOffset());
       writer.printf(
           "  %s %s (%d, %d/%d): %s",
           source == null ? "" : source.getFile().getName(),
           error.getErrorCode(),
           location.getLineNumber(),
           location.getColumnNumber(),
           error.getLength(),
           error.getMessage());
     }
   }
   Assert.fail(writer.toString());
 }
 /**
  * Return {@code true} if the two errors are equivalent.
  *
  * @param firstError the first error being compared
  * @param secondError the second error being compared
  * @return {@code true} if the two errors are equivalent
  */
 private boolean equals(AnalysisError firstError, AnalysisError secondError) {
   return firstError.getErrorCode() == secondError.getErrorCode()
       && firstError.getOffset() == secondError.getOffset()
       && firstError.getLength() == secondError.getLength()
       && equals(firstError.getSource(), secondError.getSource());
 }