Bug 734817 - Add a difference engine for processing items. r=mmecca,philipp
authorPhilipp Kewisch <mozilla@kewis.ch>
Tue, 17 Apr 2012 20:19:40 -0400
changeset 11656 5e80e777ae2d5b881f9856f65c03dbdef04ba07b
parent 11655 db79074493e33d082063b25ba2ca261da7bc6a1f
child 11657 85ab1c6c78aa08a8587fca9d02c98eb6e7cd6ef7
push id529
push userbugzilla@standard8.plus.com
push dateMon, 04 Jun 2012 19:55:55 +0000
treeherdercomm-beta@109334822255 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmmecca, philipp
bugs734817
Bug 734817 - Add a difference engine for processing items. r=mmecca,philipp
calendar/base/modules/Makefile.in
calendar/base/modules/calHashedArray.jsm
calendar/base/modules/calItemUtils.jsm
--- 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;
+    }
+};