/** * 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(); } }