@SuppressWarnings("unchecked")
  private void finishUp() {
    try {
      flushQueues();
      Logger.debug(
          LOG_TAG,
          "Have "
              + parentToChildArray.size()
              + " folders whose children might need repositioning.");
      for (Entry<String, JSONArray> entry : parentToChildArray.entrySet()) {
        String guid = entry.getKey();
        JSONArray onServer = entry.getValue();
        try {
          final long folderID = getIDForGUID(guid);
          final JSONArray inDB = new JSONArray();
          final boolean clean = getChildrenArray(folderID, false, inDB);
          final boolean sameArrays = Utils.sameArrays(onServer, inDB);

          // If the local children and the remote children are already
          // the same, then we don't need to bump the modified time of the
          // parent: we wouldn't upload a different record, so avoid the cycle.
          if (!sameArrays) {
            int added = 0;
            for (Object o : inDB) {
              if (!onServer.contains(o)) {
                onServer.add(o);
                added++;
              }
            }
            Logger.debug(LOG_TAG, "Added " + added + " items locally.");
            Logger.debug(LOG_TAG, "Untracking and bumping " + guid + "(" + folderID + ")");
            dataAccessor.bumpModified(folderID, now());
            untrackGUID(guid);
          }

          // If the arrays are different, or they're the same but not flushed to disk,
          // write them out now.
          if (!sameArrays || !clean) {
            dataAccessor.updatePositions(new ArrayList<String>(onServer));
          }
        } catch (Exception e) {
          Logger.warn(LOG_TAG, "Error repositioning children for " + guid, e);
        }
      }
    } finally {
      super.storeDone();
    }
  }
  /**
   * Retrieve the child array for a record, repositioning and updating the database as necessary.
   *
   * @param folderID The database ID of the folder.
   * @param persist True if generated positions should be written to the database. The modified time
   *     of the parent folder is only bumped if this is true.
   * @param childArray A new, empty JSONArray which will be populated with an array of GUIDs.
   * @return True if the resulting array is "clean" (i.e., reflects the content of the database).
   * @throws NullCursorException
   */
  @SuppressWarnings("unchecked")
  private boolean getChildrenArray(long folderID, boolean persist, JSONArray childArray)
      throws NullCursorException {
    trace("Calling getChildren for androidID " + folderID);
    Cursor children = dataAccessor.getChildren(folderID);
    try {
      if (!children.moveToFirst()) {
        trace("No children: empty cursor.");
        return true;
      }
      final int positionIndex = children.getColumnIndex(BrowserContract.Bookmarks.POSITION);
      final int count = children.getCount();
      Logger.debug(LOG_TAG, "Expecting " + count + " children.");

      // Sorted by requested position.
      TreeMap<Long, ArrayList<String>> guids = new TreeMap<Long, ArrayList<String>>();

      while (!children.isAfterLast()) {
        final String childGuid = getGUID(children);
        final long childPosition = getPosition(children, positionIndex);
        trace("  Child GUID: " + childGuid);
        trace("  Child position: " + childPosition);
        Utils.addToIndexBucketMap(guids, Math.abs(childPosition), childGuid);
        children.moveToNext();
      }

      // This will suffice for taking a jumble of records and indices and
      // producing a sorted sequence that preserves some kind of order --
      // from the abs of the position, falling back on cursor order (that
      // is, creation time and ID).
      // Note that this code is not intended to merge values from two sources!
      boolean changed = false;
      int i = 0;
      for (Entry<Long, ArrayList<String>> entry : guids.entrySet()) {
        long pos = entry.getKey().longValue();
        int atPos = entry.getValue().size();

        // If every element has a different index, and the indices are
        // in strict natural order, then changed will be false.
        if (atPos > 1 || pos != i) {
          changed = true;
        }

        ++i;

        for (String guid : entry.getValue()) {
          if (!forbiddenGUID(guid)) {
            childArray.add(guid);
          }
        }
      }

      if (Logger.logVerbose(LOG_TAG)) {
        // Don't JSON-encode unless we're logging.
        Logger.trace(LOG_TAG, "Output child array: " + childArray.toJSONString());
      }

      if (!changed) {
        Logger.debug(LOG_TAG, "Nothing moved! Database reflects child array.");
        return true;
      }

      if (!persist) {
        Logger.debug(LOG_TAG, "Returned array does not match database, and not persisting.");
        return false;
      }

      Logger.debug(LOG_TAG, "Generating child array required moving records. Updating DB.");
      final long time = now();
      if (0 < dataAccessor.updatePositions(childArray)) {
        Logger.debug(LOG_TAG, "Bumping parent time to " + time + ".");
        dataAccessor.bumpModified(folderID, time);
      }
      return true;
    } finally {
      children.close();
    }
  }