private void waitForAllTasksCompleted() {
    // NOTE: Wait till all queue becomes empty
    while ((batcher != null && batcher.count() > 0)
        || (pendingFutures != null && pendingFutures.size() > 0)
        || (downloadsToInsert != null && downloadsToInsert.count() > 0)) {

      // Wait for batcher completed
      if (batcher != null) {
        // if batcher delays task execution, need to wait same amount of time. (0.5 sec or 0 sec)
        try {
          Thread.sleep(batcher.getDelay());
        } catch (Exception e) {
        }
        Log.d(Log.TAG_SYNC, "batcher.waitForPendingFutures()");
        batcher.waitForPendingFutures();
      }

      // wait for pending featurs completed
      Log.d(Log.TAG_SYNC, "waitPendingFuturesCompleted()");
      waitPendingFuturesCompleted();

      // wait for downloadToInsert batcher completed
      if (downloadsToInsert != null) {
        // if batcher delays task execution, need to wait same amount of time. (1.0 sec or 0 sec)
        try {
          Thread.sleep(downloadsToInsert.getDelay());
        } catch (Exception e) {
        }
        Log.d(Log.TAG_SYNC, "downloadsToInsert.waitForPendingFutures()");
        downloadsToInsert.waitForPendingFutures();
      }
    }
  }
  // This invokes the tranformation block if one is installed and queues the resulting CBL_Revision
  private void queueDownloadedRevision(RevisionInternal rev) {

    if (revisionBodyTransformationBlock != null) {
      // Add 'file' properties to attachments pointing to their bodies:

      for (Map.Entry<String, Map<String, Object>> entry :
          ((Map<String, Map<String, Object>>) rev.getProperties().get("_attachments")).entrySet()) {
        String name = entry.getKey();
        Map<String, Object> attachment = entry.getValue();
        attachment.remove("file");
        if (attachment.get("follows") != null && attachment.get("data") == null) {
          String filePath = db.fileForAttachmentDict(attachment).getPath();
          if (filePath != null) attachment.put("file", filePath);
        }
      }

      RevisionInternal xformed = transformRevision(rev);
      if (xformed == null) {
        Log.v(Log.TAG_SYNC, "%s: Transformer rejected revision %s", this, rev);
        pendingSequences.removeSequence(rev.getSequence());
        lastSequence = pendingSequences.getCheckpointedValue();
        pauseOrResume();
        return;
      }
      rev = xformed;

      // Clean up afterwards
      Map<String, Object> attachments =
          (Map<String, Object>) rev.getProperties().get("_attachments");

      for (Map.Entry<String, Map<String, Object>> entry :
          ((Map<String, Map<String, Object>>) rev.getProperties().get("_attachments")).entrySet()) {
        Map<String, Object> attachment = entry.getValue();
        attachment.remove("file");
      }
    }

    if (rev != null && rev.getBody() != null) rev.getBody().compact();

    downloadsToInsert.queueObject(rev);
  }