1
// SimpleUniverseManager.cs
3
// GNOME Do is the legal property of its developers. Please refer to the
4
// COPYRIGHT file distributed with this source distribution.
6
// This program is free software: you can redistribute it and/or modify
7
// it under the terms of the GNU General Public License as published by
8
// the Free Software Foundation, either version 3 of the License, or
9
// (at your option) any later version.
11
// This program is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
// GNU General Public License for more details.
16
// You should have received a copy of the GNU General Public License
17
// along with this program. If not, see <http://www.gnu.org/licenses/>.
22
using System.Collections.Generic;
23
using System.Threading;
31
// Threading Heirarchy:
32
// universeLock may be locked within quickResultsLock and actionLock
33
// No other nested locks should be allowed
35
public class SimpleUniverseManager : IUniverseManager
37
private Dictionary<string, IObject> universe;
38
private Dictionary<char, Dictionary<string, IObject>> quickResults;
39
private List<IObject> actions;
40
private Thread thread;
41
private DateTime last_update = DateTime.Now;
43
private object universeLock = new object ();
44
private object quickResultsLock = new object ();
45
private object actionLock = new object ();
47
private float epsilon = 0.00001f;
49
public SimpleUniverseManager()
51
universe = new Dictionary<string, IObject> ();
52
quickResults = new Dictionary<char,Dictionary<string,IObject>> ();
53
actions = new List<IObject> ();
55
for (char key = 'a'; key <= 'z'; key++) {
56
quickResults [key] = new Dictionary<string,IObject> ();
60
public IObject[] Search (string query, Type[] searchFilter)
62
//Do.PrintPerf ("Search2 Start");
63
if (query.Length == 1) {
64
lock (quickResultsLock) {
65
char key = Convert.ToChar (query.ToLower ());
66
if (quickResults.ContainsKey (key)) {
67
//Do.PrintPerf ("Search2 End");
68
return Search (query, searchFilter, quickResults[key].Values, null);
73
if (searchFilter.Length == 1 && searchFilter[0] == typeof (IAction))
75
return Search (query, searchFilter, actions, null);
78
return Search (query, searchFilter, universe.Values, null);
81
public IObject[] Search (string query, Type[] searchFilter, IObject otherObj)
83
if (searchFilter.Length == 1 && searchFilter[0] == typeof (IAction))
85
return Search (query, searchFilter, actions, otherObj);
87
if (query.Length == 1) {
88
lock (quickResultsLock) {
89
char key = Convert.ToChar (query.ToLower ());
90
if (quickResults.ContainsKey (key))
91
return Search (query, searchFilter, quickResults[key].Values, null);
96
return Search (query, searchFilter, universe.Values, otherObj);
99
public IObject[] Search (string query, Type[] searchFilter, IEnumerable<IObject> baseArray)
101
return Search (query, searchFilter, baseArray, null);
104
public IObject[] Search (string query, Type[] searchFilter, IEnumerable<IObject> baseArray, IObject compareObj)
106
List<IObject> results = new List<IObject> ();
107
query = query.ToLower ();
109
foreach (DoObject obj in baseArray) {
110
obj.UpdateRelevance (query, compareObj as DoObject);
111
if (Math.Abs (obj.Relevance) > epsilon) {
112
if (searchFilter.Length == 0) {
115
foreach (Type t in searchFilter) {
116
if (t.IsInstanceOfType (obj.Inner)) {
125
// Ideally we would do stable sorts all the time, but quicksort is... quickest
126
// so we do a stable sort only on small lists
127
if (results.Count < 40)
128
InsertionSort (results);
132
return results.ToArray ();
135
private void InsertionSort (List<IObject> list)
138
throw new ArgumentNullException( "list" );
141
for (int j = 1; j < list.Count; j++) {
145
for (; i >= 0 && (list[i] as DoObject).CompareTo (key as DoObject) > 0; i--) {
146
list[i + 1] = list[i];
153
/// Threaded universe building
155
private void BuildUniverse ()
157
//Originally i had threaded the loading of each plugin, but they dont seem to like this...
158
if (thread != null && thread.IsAlive) return;
160
thread = new Thread (new ThreadStart (LoadUniverse));
161
thread.IsBackground = true;
167
/// Do not call inside main thread unless you really like a locked Do.
169
private void LoadUniverse ()
171
Dictionary<string, IObject> loc_universe;
172
Dictionary<char, Dictionary<string, IObject>> loc_quick;
173
List<IObject> loc_actions;
174
if (universe.Values.Count > 0) {
175
loc_universe = new Dictionary<string,IObject> ();
176
loc_quick = new Dictionary<char,Dictionary<string,IObject>> ();
177
for (char key = 'a'; key <= 'z'; key++) {
178
loc_quick [key] = new Dictionary<string,IObject> ();
180
loc_actions = new List<IObject> ();
182
loc_universe = universe;
183
loc_quick = quickResults;
184
loc_actions = actions;
187
foreach (DoAction action in PluginManager.GetActions ()) {
189
loc_universe[action.UID] = action;
190
RegisterQuickResults (loc_quick, action);
192
loc_actions.Add (action);
195
foreach (DoItemSource source in PluginManager.GetItemSources ()) {
196
source.UpdateItems ();
197
foreach (DoItem item in source.Items) {
199
loc_universe[item.UID] = item;
200
RegisterQuickResults (loc_quick, item);
205
universe = loc_universe;
206
lock (quickResultsLock)
207
quickResults = loc_quick;
209
actions = loc_actions;
215
//maxResults = (int)universe.Count/7;
216
last_update = DateTime.Now;
217
Console.WriteLine ("Universe contains {0} items.", universe.Count);
221
/// Registers quickResults into the passed dictionary of the result passed
223
/// <param name="quickResults">
224
/// A <see cref="Dictionary`2"/>
226
/// <param name="result">
227
/// A <see cref="IObject"/>
229
private void RegisterQuickResults (Dictionary<char, Dictionary<string, IObject>> quickResults, IObject result)
231
if (quickResults == null) return;
234
if (result is DoObject)
235
do_result = result as DoObject;
237
do_result = new DoObject (result);
239
lock (quickResultsLock) {
240
foreach (char key in quickResults.Keys) {
241
do_result.UpdateRelevance (key.ToString (), null);
242
if (do_result.Relevance > epsilon)
243
quickResults[key][do_result.UID] = do_result;
249
/// Deletes a result from the global quickresults dictionary
251
/// <param name="result">
252
/// A <see cref="IObject"/>
254
private void DeleteQuickResult (IObject result)
256
string UID = new DoObject (result).UID;
257
lock (quickResultsLock) {
258
foreach (Dictionary<string, IObject> list in quickResults.Values)
264
/// Add a list of IItems to the universe
266
/// <param name="items">
267
/// A <see cref="IEnumerable`1"/>
269
public void AddItems (IEnumerable<IItem> items)
271
foreach (IItem i in items) {
272
if (i is DoItem && !universe.ContainsKey ((i as DoItem).UID)) {
273
lock (universeLock) {
274
universe.Add ((i as DoItem).UID, i);
276
RegisterQuickResults (quickResults, i);
278
DoItem di = new DoItem (i);
279
if (!universe.ContainsKey (di.UID)) {
280
lock (universeLock) {
281
universe.Add (di.UID, di);
283
RegisterQuickResults (quickResults, di);
290
/// Remove a list of IItems from the universe. This removal does not prevent these
291
/// items from returning to the universe at a future date.
293
/// <param name="items">
294
/// A <see cref="IEnumerable`1"/>
296
public void DeleteItems (IEnumerable<IItem> items)
298
foreach (IItem i in items) {
300
lock (universeLock) {
301
universe.Remove ((i as DoItem).UID);
304
lock (universeLock) {
305
universe.Remove (new DoItem (i).UID);
308
DeleteQuickResult (i);
313
/// Returns the UID for an object
316
/// A <see cref="IObject"/>
319
/// A <see cref="System.String"/>
321
public string UIDForObject (IObject o)
324
return (o as DoObject).UID;
325
return new DoObject (o).UID;
329
/// Attempts to get an Object for a given UID.
331
/// <param name="UID">
332
/// A <see cref="System.String"/>
334
/// <param name="item">
335
/// A <see cref="IObject"/>
337
public void TryGetObjectForUID (string UID, out IObject item)
339
if (universe.ContainsKey (UID)) {
340
item = (universe[UID] as DoObject).Inner;
348
/// Causes the universe to be rebuilt in the background
350
public void Reload ()
352
Console.WriteLine ("Reload");
356
public void Initialize ()
359
GLib.Timeout.Add (5 * 60 * 1000, delegate {
360
if (DBus.PowerState.OnBattery () &&
361
DateTime.Now.Subtract (last_update).TotalMinutes < 15)