2009-06-02 08:28:03 +04:00
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is Snowl.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is Mozilla.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2009
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Myk Melez <myk@mozilla.org>
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
let EXPORTED_SYMBOLS = ["Collection2"];
|
|
|
|
|
|
|
|
const Cc = Components.classes;
|
|
|
|
const Ci = Components.interfaces;
|
|
|
|
const Cr = Components.results;
|
|
|
|
const Cu = Components.utils;
|
|
|
|
|
|
|
|
// modules that are generic
|
|
|
|
Cu.import("resource://snowl/modules/URI.js");
|
|
|
|
Cu.import("resource://snowl/modules/log4moz.js");
|
2009-06-03 01:18:00 +04:00
|
|
|
Cu.import("resource://snowl/modules/Sync.js");
|
2009-06-02 08:28:03 +04:00
|
|
|
|
|
|
|
// modules that are Snowl-specific
|
|
|
|
Cu.import("resource://snowl/modules/constants.js");
|
|
|
|
Cu.import("resource://snowl/modules/datastore.js");
|
|
|
|
Cu.import("resource://snowl/modules/message.js");
|
|
|
|
Cu.import("resource://snowl/modules/service.js");
|
|
|
|
Cu.import("resource://snowl/modules/utils.js");
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A set of messages. Use this to retrieve messages from the datastore.
|
|
|
|
* This implementation differs from the one in collection.js in that it:
|
|
|
|
* * doesn't support grouping;
|
2009-06-03 01:18:00 +04:00
|
|
|
* * queries asynchronously;
|
|
|
|
* * retrieves messages as complete objects;
|
|
|
|
* * provides a custom iterator.
|
2009-06-02 08:28:03 +04:00
|
|
|
*
|
|
|
|
* To use this object, create a new instance, passing the constructor
|
2009-06-03 01:18:00 +04:00
|
|
|
* the criteria that define the set of objects to retrieve. The constructor
|
2009-06-02 08:28:03 +04:00
|
|
|
* will retrieve messages without blocking execution of events on the same
|
2009-06-03 01:18:00 +04:00
|
|
|
* thread, although the call will appear synchronous to the caller.
|
2009-06-02 08:28:03 +04:00
|
|
|
*
|
2009-06-03 01:18:00 +04:00
|
|
|
* let collection = new Collection2();
|
|
|
|
* for each (let message in collection)
|
|
|
|
* dump("retrieved message " + message.id + "\n");
|
2009-06-02 08:28:03 +04:00
|
|
|
*/
|
2009-06-03 03:00:52 +04:00
|
|
|
function Collection2(args) {
|
|
|
|
// Extract values from arguments and assign them to member properties.
|
|
|
|
this.constraints = "constraints" in args ? args.constraints : [];
|
|
|
|
if ("order" in args) this.order = args.order;
|
|
|
|
if ("limit" in args) this.limit = args.limit;
|
|
|
|
|
|
|
|
// Execute the query so its results are available once the constructor returns.
|
2009-06-06 04:36:11 +04:00
|
|
|
Sync(this.execute, this)();
|
2009-06-02 08:28:03 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
Collection2.prototype = {
|
2009-06-03 03:00:52 +04:00
|
|
|
//**************************************************************************//
|
|
|
|
// Properties
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An array of Constraint objects with expression and parameters properties
|
|
|
|
* that let you constrain the messages returned by the datastore, f.e.:
|
|
|
|
*
|
|
|
|
* [ { expression: "messages.timestamp > :timestamp",
|
|
|
|
* parameters: { name: "timestamp", value: 2454985 } } ]
|
|
|
|
*
|
|
|
|
* Constraint.parameters is optional.
|
|
|
|
*/
|
|
|
|
constraints: null,
|
|
|
|
|
|
|
|
order: null,
|
|
|
|
limit: null,
|
|
|
|
|
|
|
|
|
2009-06-02 08:28:03 +04:00
|
|
|
//**************************************************************************//
|
|
|
|
// Shortcuts
|
|
|
|
|
|
|
|
get _log() {
|
|
|
|
let log = Log4Moz.repository.getLogger("Snowl.Collection2");
|
|
|
|
this.__defineGetter__("_log", function() log);
|
|
|
|
return this._log;
|
|
|
|
},
|
|
|
|
|
|
|
|
execute: function(callback) {
|
|
|
|
this._callback = callback;
|
|
|
|
this._pendingStatement =
|
|
|
|
this._statement._statement.statement.executeAsync(this);
|
|
|
|
this._log.info("pending statement: " + this._pendingStatement);
|
|
|
|
},
|
|
|
|
|
2009-06-03 01:18:00 +04:00
|
|
|
|
|
|
|
//**************************************************************************//
|
|
|
|
// mozIStorageStatementCallback
|
|
|
|
|
2009-06-02 08:28:03 +04:00
|
|
|
handleResult: function(resultSet) {
|
|
|
|
this._log.info("handleResult: " + resultSet);
|
|
|
|
this._resultSet = resultSet;
|
|
|
|
},
|
|
|
|
|
|
|
|
handleError: function(error) {
|
|
|
|
this._log.info("handleError: " + error);
|
|
|
|
},
|
|
|
|
|
|
|
|
handleCompletion: function(reason) {
|
|
|
|
this._log.info("handleCompletion: " + reason);
|
|
|
|
(this._callback)();
|
|
|
|
},
|
|
|
|
|
2009-06-03 01:18:00 +04:00
|
|
|
|
2009-06-02 08:28:03 +04:00
|
|
|
get _statement() {
|
|
|
|
let columns = [
|
|
|
|
"messages.id AS messageID",
|
|
|
|
"messages.sourceID",
|
|
|
|
"messages.authorID",
|
|
|
|
"messages.subject",
|
|
|
|
"messages.link",
|
|
|
|
"messages.timestamp",
|
|
|
|
"messages.read",
|
|
|
|
"messages.received",
|
2009-06-08 03:54:56 +04:00
|
|
|
"identities.id AS identities_id",
|
|
|
|
"identities.sourceID AS identities_sourceID",
|
|
|
|
"identities.externalID AS identities_externalID",
|
|
|
|
"identities.personID AS identities_personID",
|
|
|
|
"people.id AS people_id",
|
|
|
|
"people.name AS people_name",
|
|
|
|
"people.placeID AS people_placeID",
|
|
|
|
"people.homeURL AS people_homeURL",
|
|
|
|
"people.iconURL AS people_iconURL",
|
2009-06-02 08:28:03 +04:00
|
|
|
"parts.id AS partID",
|
|
|
|
"parts.content",
|
|
|
|
"parts.mediaType",
|
|
|
|
"parts.baseURI",
|
|
|
|
"parts.languageTag"
|
|
|
|
];
|
|
|
|
|
|
|
|
let query =
|
2009-06-08 03:54:56 +04:00
|
|
|
"SELECT " + columns.join(", ") + " FROM sources " +
|
|
|
|
"JOIN messages ON sources.id = messages.sourceID " +
|
|
|
|
"LEFT JOIN identities ON messages.authorID = identities.id " +
|
|
|
|
"LEFT JOIN people ON identities.personID = people.id " +
|
2009-06-02 08:28:03 +04:00
|
|
|
"LEFT JOIN parts AS parts ON messages.id = parts.messageID " +
|
|
|
|
|
|
|
|
// This partType condition has to be in the constraint for the LEFT JOIN
|
|
|
|
// to the parts table because if it was in the WHERE clause it would
|
|
|
|
// exclude messages without a content part, whereas we want to retrieve
|
|
|
|
// all messages whether or not they have a content part.
|
|
|
|
"AND parts.partType = " + PART_TYPE_CONTENT;
|
|
|
|
|
2009-06-03 03:00:52 +04:00
|
|
|
let conditions = [];
|
|
|
|
for each (let constraint in this.constraints)
|
|
|
|
conditions.push(constraint.expression);
|
|
|
|
|
|
|
|
if (conditions.length > 0)
|
|
|
|
query += " WHERE " + conditions.join(" AND ");
|
|
|
|
|
|
|
|
if (this.order)
|
|
|
|
query += " ORDER BY " + this.order;
|
|
|
|
|
|
|
|
if (this.limit)
|
|
|
|
query += " LIMIT " + this.limit;
|
2009-06-02 08:28:03 +04:00
|
|
|
|
|
|
|
let statement = SnowlDatastore.createStatement(query);
|
|
|
|
|
2009-06-03 03:00:52 +04:00
|
|
|
for each (let constraint in this.constraints)
|
|
|
|
if ("parameters" in constraint)
|
|
|
|
for (let [name, value] in Iterator(constraint.parameters))
|
|
|
|
statement.params[name] = value;
|
|
|
|
|
|
|
|
this._log.info(query);
|
|
|
|
|
2009-06-02 08:28:03 +04:00
|
|
|
return statement;
|
|
|
|
},
|
|
|
|
|
2009-06-03 01:18:00 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* An iterator across the messages in the collection. Allows callers
|
|
|
|
* to iterate messages via |for each... in|, i.e.:
|
|
|
|
*
|
|
|
|
* let collection = new Collection2();
|
|
|
|
* for each (let message in collection) ...
|
|
|
|
*/
|
2009-06-02 08:28:03 +04:00
|
|
|
__iterator__: function(wantKeys) {
|
|
|
|
let row;
|
2009-06-03 01:18:00 +04:00
|
|
|
while ((row = this._resultSet.getNextRow())) {
|
2009-06-02 08:28:03 +04:00
|
|
|
let content = null;
|
|
|
|
if (row.getResultByName("partID")) {
|
|
|
|
content = Cc["@mozilla.org/feed-textconstruct;1"].
|
|
|
|
createInstance(Ci.nsIFeedTextConstruct);
|
|
|
|
content.text = row.getResultByName("content");
|
|
|
|
content.type = TEXT_CONSTRUCT_TYPES[row.getResultByName("mediaType")];
|
|
|
|
content.base = URI.get(row.getResultByName("baseURI"));
|
|
|
|
content.lang = row.getResultByName("languageTag");
|
|
|
|
}
|
|
|
|
|
2009-06-08 03:54:56 +04:00
|
|
|
let author;
|
|
|
|
if (row.authorID) {
|
|
|
|
let person = new SnowlPerson(row.people_id,
|
|
|
|
row.people_name,
|
|
|
|
row.people_placeID,
|
|
|
|
row.people_homeURL,
|
|
|
|
row.people_iconURL);
|
|
|
|
let identity = new SnowlIdentity(row.identities_id,
|
|
|
|
row.identities_sourceID,
|
|
|
|
row.identities_externalID,
|
|
|
|
person);
|
|
|
|
author = identity;
|
|
|
|
}
|
|
|
|
|
2009-06-02 08:28:03 +04:00
|
|
|
let message = new SnowlMessage({
|
|
|
|
id: row.getResultByName("messageID"),
|
|
|
|
sourceID: row.getResultByName("sourceID"),
|
|
|
|
source: SnowlService.sourcesByID[row.getResultByName("sourceID")],
|
|
|
|
subject: row.getResultByName("subject"),
|
|
|
|
link: row.getResultByName("link"),
|
|
|
|
timestamp: SnowlDateUtils.julianToJSDate(row.getResultByName("timestamp")),
|
|
|
|
read: row.getResultByName("read"),
|
|
|
|
received: SnowlDateUtils.julianToJSDate(row.getResultByName("received")),
|
2009-06-08 03:54:56 +04:00
|
|
|
author: author,
|
2009-06-02 08:28:03 +04:00
|
|
|
content: content
|
|
|
|
});
|
|
|
|
|
|
|
|
yield message;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|