Bug 734817 - Add a difference engine for processing items. r=mmecca,philipp
--- a/calendar/base/modules/Makefile.in
+++ b/calendar/base/modules/Makefile.in
@@ -44,16 +44,17 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = calbase
EXTRA_JS_MODULES = \
calAlarmUtils.jsm \
calAuthUtils.jsm \
calHashedArray.jsm \
+ calItemUtils.jsm \
calIteratorUtils.jsm \
calItipUtils.jsm \
calPrintUtils.jsm \
calProviderUtils.jsm \
calUtils.jsm \
$(NULL)
include $(topsrcdir)/config/rules.mk
--- a/calendar/base/modules/calHashedArray.jsm
+++ b/calendar/base/modules/calHashedArray.jsm
@@ -190,16 +190,20 @@ cal.HashedArray.prototype = {
* swapped if from > to.
*
* @param from (optional) The index to start indexing from. If left
* out, defaults to 0.
* @param to (optional) The index to end indexing on. If left out,
* defaults to the array length.
*/
reindex: function reindex(from, to) {
+ if (this.mArray.length == 0) {
+ return;
+ }
+
from = (from === undefined ? 0 : from);
to = (to === undefined ? this.mArray.length - 1 : to);
from = Math.min(this.mArray.length - 1, Math.max(0, from));
to = Math.min(this.mArray.length - 1, Math.max(0, to));
if (from > to) {
let tmp = from;
new file mode 100644
--- /dev/null
+++ b/calendar/base/modules/calItemUtils.jsm
@@ -0,0 +1,176 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var EXPORTED_SYMBOLS = ["itemDiff"];
+"use strict";
+
+Components.utils.import("resource://calendar/modules/calHashedArray.jsm");
+
+/**
+ * Given two sets of items, find out which items were added, changed or
+ * removed.
+ *
+ * The general flow is to first use load/load1 methods to load the engine with
+ * the first set of items, then use difference/difference1 to load the set of
+ * items to diff against. Afterwards, call the complete method to tell the
+ * engine that no more items are coming.
+ *
+ * You can then access the mAddedItems/mModifiedItems/mDeletedItems attributes to
+ * get the items that were changed during the process.
+ */
+function itemDiff() {
+ this.reset();
+}
+
+itemDiff.prototype = {
+ STATE_INITIAL: 1,
+ STATE_LOADING: 2,
+ STATE_DIFFERING: 4,
+ STATE_COMPLETED: 8,
+
+ state: 1,
+ mInitialItems: null,
+
+ mModifiedItems: null,
+ mModifiedOldItems: null,
+ mAddedItems: null,
+ mDeletedItems: null,
+
+ /**
+ * Expect the difference engine to be in the given state.
+ *
+ * @param aState The state to be in
+ */
+ _expectState: function _expectState(aState) {
+ if ((this.state & aState) == 0) {
+ throw new Error("itemDiff method " + arguments.callee.caller.name +
+ " called while in unexpected state " + this.state);
+ }
+ },
+
+ /**
+ * Load the difference engine with one item, see load.
+ *
+ * @param item The item to load
+ */
+ load1: function load1(item) {
+ this.load([item]);
+ },
+
+ /**
+ * Loads an array of items. This step cannot be executed
+ * after calling the difference methods.
+ *
+ * @param items The array of items to load
+ */
+ load: function load(items) {
+ this._expectState(this.STATE_INITIAL | this.STATE_LOADING);
+
+ for each (let item in items) {
+ this.mInitialItems[item.hashId] = item;
+ }
+
+ this.state = this.STATE_LOADING;
+ },
+
+ /**
+ * Calculates the difference for the passed item, see difference.
+ *
+ * @param item The item to calculate difference with
+ */
+ difference1: function difference1(item) {
+ this.difference([item]);
+ },
+
+ /**
+ * Calculate the difference for the array of items. This method should be
+ * called after all load methods and before the complete method.
+ *
+ * @param items The array of items to calculate difference with
+ */
+ difference: function difference(items) {
+ this._expectState(this.STATE_INITIAL | this.STATE_LOADING | this.STATE_DIFFERING);
+
+ this.mModifiedOldItems.startBatch();
+ this.mModifiedItems.startBatch();
+ this.mAddedItems.startBatch();
+
+ for each (let item in items) {
+ if (item.hashId in this.mInitialItems) {
+ let oldItem = this.mInitialItems[item.hashId];
+ this.mModifiedOldItems.addItem(oldItem);
+ this.mModifiedItems.addItem(item);
+ } else {
+ this.mAddedItems.addItem(item);
+ }
+ delete this.mInitialItems[item.hashId];
+ }
+
+ this.mModifiedOldItems.endBatch();
+ this.mModifiedItems.endBatch();
+ this.mAddedItems.endBatch();
+
+ this.state = this.STATE_DIFFERING;
+ },
+
+ /**
+ * Tell the engine that all load and difference calls have been made, this
+ * makes sure that all item states are correctly returned.
+ */
+ complete: function complete() {
+ this._expectState(this.STATE_INITIAL | this.STATE_LOADING | this.STATE_DIFFERING);
+
+ this.mDeletedItems.startBatch();
+
+ for each (let item in this.mInitialItems) {
+ this.mDeletedItems.addItem(item);
+ }
+
+ this.mDeletedItems.endBatch();
+ this.mInitialItems = {};
+
+ this.state = this.STATE_COMPLETED;
+ },
+
+ /** @return a HashedArray containing the new version of the modified items */
+ get modifiedItems() {
+ this._expectState(this.STATE_COMPLETED);
+ return this.mModifiedItems;
+ },
+
+ /** @return a HashedArray containing the old version of the modified items */
+ get modifiedOldItems() {
+ this._expectState(this.STATE_COMPLETED);
+ return this.mModifiedOldItems;
+ },
+
+ /** @return a HashedArray containing added items */
+ get addedItems() {
+ this._expectState(this.STATE_COMPLETED);
+ return this.mAddedItems;
+ },
+
+ /** @return a HashedArray containing deleted items */
+ get deletedItems() {
+ this._expectState(this.STATE_COMPLETED);
+ return this.mDeletedItems;
+ },
+
+ /** @return the number of loaded items */
+ get count() {
+ return Object.keys(this.mInitialItems).length;
+ },
+
+ /**
+ * Resets the difference engine to its initial state.
+ */
+ reset: function reset() {
+ this.mInitialItems = {};
+ this.mModifiedItems = new cal.HashedArray();
+ this.mModifiedOldItems = new cal.HashedArray();
+ this.mAddedItems = new cal.HashedArray();
+ this.mDeletedItems = new cal.HashedArray();
+ this.state = this.STATE_INITIAL;
+ }
+};