/**
   * Canonicalize an ordered set of exceptions. In the canonical form, none of the RVVExceptions
   * have any received versions.
   *
   * @param exceptions
   * @return The canonicalized set of exceptions.
   */
  protected List<RVVException> canonicalExceptions(List<RVVException> exceptions) {
    LinkedList<RVVException> canon = new LinkedList<RVVException>();
    if (exceptions != null) {
      // Iterate through the set of exceptions
      for (RVVException exception : exceptions) {
        if (exception.isEmpty()) {
          canon.add(exception);
        } else {
          long previous = exception.previousVersion;
          // Iterate through the set of received versions for this exception
          int insertAt = canon.size();
          for (ReceivedVersionsIterator it = exception.receivedVersionsIterator(); it.hasNext(); ) {
            Long received = it.next();
            // If we find a gap between the previous received version and the
            // next received version, add an exception.
            if (received != previous + 1) {
              canon.add(insertAt, RVVException.createException(previous, received));
            }
            // move the previous reference
            previous = received;
          }

          // if there is a gap between the last received version and the next
          // version, add an exception
          // this also handles the case where the RVV has no received versions,
          // because previous==exception.previousVersion in that case.
          if (exception.nextVersion != previous + 1) {
            canon.add(insertAt, RVVException.createException(previous, exception.nextVersion));
          }
        }
      }
    }
    return canon;
  }