/**
   * Checks a set of tags for matching throws.
   *
   * @param tags the tags to check
   * @param throwsList the throws to check
   * @param reportExpectedTags whether we should report if do not find expected tag
   */
  private void checkThrowsTags(
      List<JavadocTag> tags, List<ExceptionInfo> throwsList, boolean reportExpectedTags) {
    // Loop over the tags, checking to see they exist in the throws.
    // The foundThrows used for performance only
    final Set<String> foundThrows = Sets.newHashSet();
    final ListIterator<JavadocTag> tagIt = tags.listIterator();
    while (tagIt.hasNext()) {
      final JavadocTag tag = tagIt.next();

      if (!tag.isThrowsTag()) {
        continue;
      }
      tagIt.remove();

      // Loop looking for matching throw
      final String documentedEx = tag.getFirstArg();
      final Token token = new Token(tag.getFirstArg(), tag.getLineNo(), tag.getColumnNo());
      final AbstractClassInfo documentedCI = createClassInfo(token, getCurrentClassName());
      final boolean found =
          foundThrows.contains(documentedEx) || isInThrows(throwsList, documentedCI, foundThrows);

      // Handle extra JavadocTag.
      if (!found) {
        boolean reqd = true;
        if (allowUndeclaredRTE) {
          reqd = !isUnchecked(documentedCI.getClazz());
        }

        if (reqd && validateThrows) {
          log(
              tag.getLineNo(),
              tag.getColumnNo(),
              MSG_UNUSED_TAG,
              JavadocTagInfo.THROWS.getText(),
              tag.getFirstArg());
        }
      }
    }
    // Now dump out all throws without tags :- unless
    // the user has chosen to suppress these problems
    if (!allowMissingThrowsTags && reportExpectedTags) {
      for (ExceptionInfo ei : throwsList) {
        if (!ei.isFound()) {
          final Token fi = ei.getName();
          log(
              fi.getLineNo(),
              fi.getColumnNo(),
              MSG_EXCPECTED_TAG,
              JavadocTagInfo.THROWS.getText(),
              fi.getText());
        }
      }
    }
  }
  /**
   * Verifies that documented exception is in throws.
   *
   * @param throwsList list of throws
   * @param documentedCI documented exception class info
   * @param foundThrows previously found throws
   * @return true if documented exception is in throws.
   */
  private boolean isInThrows(
      List<ExceptionInfo> throwsList, AbstractClassInfo documentedCI, Set<String> foundThrows) {
    boolean found = false;
    ExceptionInfo foundException = null;

    // First look for matches on the exception name
    final ListIterator<ExceptionInfo> throwIt = throwsList.listIterator();
    while (!found && throwIt.hasNext()) {
      final ExceptionInfo ei = throwIt.next();

      if (ei.getName().getText().equals(documentedCI.getName().getText())) {
        found = true;
        foundException = ei;
      }
    }

    // Now match on the exception type
    final ListIterator<ExceptionInfo> exceptionInfoIt = throwsList.listIterator();
    while (!found && exceptionInfoIt.hasNext()) {
      final ExceptionInfo ei = exceptionInfoIt.next();

      if (documentedCI.getClazz() == ei.getClazz()) {
        found = true;
        foundException = ei;
      } else if (allowThrowsTagsForSubclasses) {
        found = isSubclass(documentedCI.getClazz(), ei.getClazz());
      }
    }

    if (foundException != null) {
      foundException.setFound();
      foundThrows.add(documentedCI.getName().getText());
    }

    return found;
  }
 /** @return class for this exception */
 final Class<?> getClazz() {
   return classInfo.getClazz();
 }
 /** @return exception's name */
 final Token getName() {
   return classInfo.getName();
 }