/**
   * 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;
  }
 public synchronized long getVersion() {
   RVVException e = null;
   List<RVVException> exs = getExceptions();
   if (!exs.isEmpty()) {
     e = exs.get(0);
   }
   if (isSpecialException(e, this)) {
     return e.getHighestReceivedVersion();
   } else {
     return this.version;
   }
 }
 /** Add an exception that is older than this.bitSetVersion. */
 protected synchronized void addException(long previousVersion, long nextVersion) {
   if (this.exceptions == null) {
     this.exceptions = new LinkedList<RVVException>();
   }
   int i = 0;
   for (Iterator<RVVException> it = this.exceptions.iterator(); it.hasNext(); i++) {
     RVVException e = it.next();
     if (previousVersion >= e.nextVersion) {
       RVVException except = RVVException.createException(previousVersion, nextVersion);
       this.exceptions.add(i, except);
       return;
     }
   }
   this.exceptions.add(RVVException.createException(previousVersion, nextVersion));
 }
 @Override
 public synchronized RegionVersionHolder<T> clone() {
   RegionVersionHolder<T> clone = new RegionVersionHolder<T>(this.version);
   clone.id = this.id;
   clone.isDepartedMember = this.isDepartedMember;
   if (this.exceptions != null) {
     clone.exceptions = new LinkedList<RVVException>();
     for (RVVException e : this.exceptions) {
       clone.exceptions.add(e.clone());
     }
   }
   if (this.bitSet != null) {
     clone.bitSet = (BitSet) this.bitSet.clone();
     clone.bitSetVersion = this.bitSetVersion;
     clone.mergeBitSet();
   }
   return clone;
 }
  /**
   * Initialize this version holder from another version holder This is called during GII.
   *
   * <p>It's more likely that the other holder has seen most of the versions, and this version
   * holder only has a few updates that happened since the GII started. So we apply our seen
   * versions to the other version holder and then initialize this version holder from the other
   * version holder.
   */
  public synchronized void initializeFrom(RegionVersionHolder<T> source) {
    // Make sure the bitsets are merged in both the source
    // and this vector
    mergeBitSet();

    RegionVersionHolder<T> other = source.clone();
    other.mergeBitSet();
    // Get a copy of the local version and exceptions
    long myVersion = this.version;

    // initialize our version and exceptions to match the others
    this.exceptions = other.exceptions;
    this.version = other.version;

    // Initialize the bit set to be empty. Merge bit set should
    // have already done this, but just to be sure.
    if (this.bitSet != null) {
      this.bitSetVersion = this.version;
      // Make sure the bit set is empty except for the first, bit, indicating
      // that the version has been received.
      this.bitSet.set(0);
    }

    // Now if this.version/exceptions overlap with myVersion/myExceptions, use this'
    // The only case needs special handling is: if myVersion is newer than this.version,
    // should create an exception (this.version+1, myversion) and set this.version=myversion
    if (myVersion > this.version) {
      RVVException e = RVVException.createException(this.version, myVersion + 1);
      // add special exception
      if (this.exceptions == null) {
        this.exceptions = new LinkedList<RVVException>();
      }
      int i = 0;
      for (RVVException exception : this.exceptions) {
        if (e.compareTo(exception) >= 0) {
          break;
        }
        i++;
      }
      this.exceptions.add(i, e);
      this.version = myVersion;
    }
  }
 /* (non-Javadoc)
  * @see com.gemstone.gemfire.DataSerializable#fromData(java.io.DataInput)
  */
 public void fromData(DataInput in) throws IOException {
   this.version = InternalDataSerializer.readUnsignedVL(in);
   int size = (int) InternalDataSerializer.readUnsignedVL(in);
   this.isDepartedMember = in.readBoolean();
   if (size > 0) {
     this.exceptions = new LinkedList<RVVException>();
     for (int i = 0; i < size; i++) {
       RVVException e = RVVException.createException(in);
       this.exceptions.add(e);
     }
   }
 }
 /**
  * Returns true if this version hold has an exception in the exception list for the given version
  * number.
  *
  * <p>This differs from contains because it returns true if v is greater than the last seen
  * version for this holder.
  */
 synchronized boolean hasExceptionFor(long v) {
   if (this.bitSet != null && v >= this.bitSetVersion) {
     if (v > this.bitSetVersion + this.bitSet.length()) {
       return false;
     }
     return this.bitSet.get((int) (v - this.bitSetVersion));
   }
   if (this.exceptions == null) {
     return false;
   }
   for (Iterator<RVVException> it = this.exceptions.iterator(); it.hasNext(); ) {
     RVVException e = it.next();
     if (e.nextVersion <= v) {
       return false; // there is no RVVException for this version
     }
     if (e.previousVersion < v && v < e.nextVersion) {
       return !e.contains(v);
     }
   }
   return false;
 }
 /** add a version that is older than this.bitSetVersion */
 private void addOlderVersion(long missingVersion) {
   // exceptions iterate in reverse order on their previousVersion variable
   if (this.exceptions == null) {
     return;
   }
   int i = 0;
   for (Iterator<RVVException> it = this.exceptions.iterator(); it.hasNext(); ) {
     RVVException e = it.next();
     if (e.nextVersion <= missingVersion) {
       return; // there is no RVVException for this version
     }
     if (e.previousVersion < missingVersion && missingVersion < e.nextVersion) {
       String fine = null;
       if (logger.isTraceEnabled(LogMarker.RVV)) {
         fine = e.toString();
       }
       e.add(missingVersion);
       if (e.isFilled()) {
         if (fine != null) {
           logger.trace(LogMarker.RVV, "Filled exception {}", fine);
         }
         it.remove();
       } else if (e.shouldChangeForm()) {
         this.exceptions.set(i, e.changeForm());
       }
       if (this.exceptions.isEmpty()) {
         this.exceptions = null;
       }
       return;
     }
     i++;
   }
 }