/**
   * Build a list of {@link ContentProviderOperation} that will transform all the "before" {@link
   * Entity} states into the modified state which all {@link EntityDelta} objects represent. This
   * method specifically creates any {@link AggregationExceptions} rules needed to groups edits
   * together.
   */
  public ArrayList<ContentProviderOperation> buildDiff() {
    final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();

    final long rawContactId = this.findRawContactId();
    int firstInsertRow = -1;

    // First pass enforces versions remain consistent
    for (EntityDelta delta : this) {
      delta.buildAssert(diff);
    }

    final int assertMark = diff.size();
    int backRefs[] = new int[size()];

    int rawContactIndex = 0;

    // Second pass builds actual operations
    for (EntityDelta delta : this) {
      final int firstBatch = diff.size();
      backRefs[rawContactIndex++] = firstBatch;
      delta.buildDiff(diff);

      // Only create rules for inserts
      if (!delta.isContactInsert()) continue;

      // If we are going to split all contacts, there is no point in first combining them
      if (mSplitRawContacts) continue;

      if (rawContactId != -1) {
        // Has existing contact, so bind to it strongly
        final Builder builder = beginKeepTogether();
        builder.withValue(AggregationExceptions.RAW_CONTACT_ID1, rawContactId);
        builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID2, firstBatch);
        diff.add(builder.build());

      } else if (firstInsertRow == -1) {
        // First insert case, so record row
        firstInsertRow = firstBatch;

      } else {
        // Additional insert case, so point at first insert
        final Builder builder = beginKeepTogether();
        builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID1, firstInsertRow);
        builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID2, firstBatch);
        diff.add(builder.build());
      }
    }

    if (mSplitRawContacts) {
      buildSplitContactDiff(diff, backRefs);
    }

    // No real changes if only left with asserts
    if (diff.size() == assertMark) {
      diff.clear();
    }

    return diff;
  }
 /**
  * Search all contained {@link EntityDelta} for the first one with an existing {@link
  * RawContacts#_ID} value. Usually used when creating {@link AggregationExceptions} during an
  * update.
  */
 public long findRawContactId() {
   for (EntityDelta delta : this) {
     final Long rawContactId = delta.getValues().getAsLong(RawContacts._ID);
     if (rawContactId != null && rawContactId >= 0) {
       return rawContactId;
     }
   }
   return -1;
 }
 /** Find {@link RawContacts#_ID} of the requested {@link EntityDelta}. */
 public Long getRawContactId(int index) {
   if (index >= 0 && index < this.size()) {
     final EntityDelta delta = this.get(index);
     final ValuesDelta values = delta.getValues();
     if (values.isVisible()) {
       return values.getAsLong(RawContacts._ID);
     }
   }
   return null;
 }
  /**
   * Merge the "after" values from the given {@link EntitySet}, discarding any previous "after"
   * states. This is typically used when re-parenting user edits onto an updated {@link EntitySet}.
   */
  public static EntitySet mergeAfter(EntitySet local, EntitySet remote) {
    if (local == null) local = new EntitySet();

    // For each entity in the remote set, try matching over existing
    for (EntityDelta remoteEntity : remote) {
      final Long rawContactId = remoteEntity.getValues().getId();

      // Find or create local match and merge
      final EntityDelta localEntity = local.getByRawContactId(rawContactId);
      final EntityDelta merged = EntityDelta.mergeAfter(localEntity, remoteEntity);

      if (localEntity == null && merged != null) {
        // No local entry before, so insert
        local.add(merged);
      }
    }

    return local;
  }
  public ValuesDelta getSuperPrimaryEntry(final String mimeType) {
    ValuesDelta primary = null;
    ValuesDelta randomEntry = null;
    for (EntityDelta delta : this) {
      final ArrayList<ValuesDelta> mimeEntries = delta.getMimeEntries(mimeType);
      if (mimeEntries == null) return null;

      for (ValuesDelta entry : mimeEntries) {
        if (entry.isSuperPrimary()) {
          return entry;
        } else if (primary == null && entry.isPrimary()) {
          primary = entry;
        } else if (randomEntry == null) {
          randomEntry = entry;
        }
      }
    }
    // When no direct super primary, return something
    if (primary != null) {
      return primary;
    }
    return randomEntry;
  }
 /**
  * Create an {@link EntitySet} based on {@link Contacts} specified by the given query parameters.
  * This closes the {@link EntityIterator} when finished, so it doesn't subscribe to updates.
  */
 public static EntitySet fromQuery(
     ContentResolver resolver, String selection, String[] selectionArgs, String sortOrder) {
   EntityIterator iterator = null;
   final EntitySet state = new EntitySet();
   try {
     // Perform background query to pull contact details
     iterator =
         resolver.queryEntities(RawContacts.CONTENT_URI, selection, selectionArgs, sortOrder);
     while (iterator.hasNext()) {
       // Read all contacts into local deltas to prepare for edits
       final Entity before = iterator.next();
       final EntityDelta entity = EntityDelta.fromBefore(before);
       state.add(entity);
     }
   } catch (RemoteException e) {
     throw new IllegalStateException("Problem querying contact details", e);
   } finally {
     if (iterator != null) {
       iterator.close();
     }
   }
   return state;
 }