Tokenize 001234 as 1234. Include a testing function in NoiseFilter to figure out...
[beagle.git] / beagled / QueryResult.cs
blob7f8e5274ab2369a899f233ac9a68ae72b6ae8ffe
1 //
2 // QueryResult.cs
3 //
4 // Copyright (C) 2004 Novell, Inc.
5 //
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a
9 // copy of this software and associated documentation files (the "Software"),
10 // to deal in the Software without restriction, including without limitation
11 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 // and/or sell copies of the Software, and to permit persons to whom the
13 // Software is furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 // DEALINGS IN THE SOFTWARE.
28 using System;
29 using System.Collections;
30 using System.Diagnostics;
31 using System.Threading;
32 using Beagle.Util;
34 namespace Beagle.Daemon {
36 public class QueryResult : IQueryResult, IDisposable {
38 public delegate void StartedHandler (QueryResult source);
39 public event StartedHandler StartedEvent;
41 public delegate void HitsAddedHandler (QueryResult source, ICollection someHits, int total_results);
42 public event HitsAddedHandler HitsAddedEvent;
44 public delegate void HitsSubtractedHandler (QueryResult source, ICollection someUris);
45 public event HitsSubtractedHandler HitsSubtractedEvent;
47 public delegate void FinishedHandler (QueryResult source);
48 public event FinishedHandler FinishedEvent;
50 //////////////////////////////////
52 int workers = 0;
53 bool cancelled = false;
54 Hashtable uri_hash = UriFu.NewHashtable ();
55 DateTime started_time;
56 DateTime finished_time;
57 Hashtable per_worker_started_time = new Hashtable ();
58 bool is_index_listener = false;
61 public QueryResult ()
66 //////////////////////////////////
68 public void Dispose ()
70 lock (this) {
71 if (cancelled)
72 return;
73 cancelled = true;
77 //////////////////////////////////
79 public bool Active {
80 get { return workers > 0 && ! cancelled; }
83 public bool Cancelled {
84 get { return cancelled; }
87 public bool IsIndexListener {
88 get { return is_index_listener; }
89 set { is_index_listener = value; }
92 public void Cancel ()
94 lock (this) {
95 if (cancelled)
96 return;
97 cancelled = true;
101 public void Add (ICollection some_hits)
103 Add (some_hits, -1);
106 // Note: some_hits is allowed to contain null.
107 // They are silently ignored.
108 public void Add (ICollection some_hits, int total_results)
110 lock (this) {
111 if (cancelled)
112 return;
114 Debug.Assert (workers > 0, "Adding Hits to idle QueryResult");
116 if (some_hits.Count == 0)
117 return;
119 if (IsIndexListener) {
120 if (HitsAddedEvent != null)
121 HitsAddedEvent (this, some_hits, total_results);
122 return;
125 // Be careful not to report the same hit twice.
126 ArrayList hits_to_report;
127 hits_to_report = new ArrayList ();
128 foreach (Hit hit in some_hits) {
129 if (hit != null && ! uri_hash.Contains (hit.Uri)) {
130 uri_hash [hit.Uri] = hit;
131 hits_to_report.Add (hit);
135 if (HitsAddedEvent != null && hits_to_report.Count > 0)
136 HitsAddedEvent (this, hits_to_report, total_results);
140 // Note: some_uris is allowed to contain null.
141 // They are silently ignored.
142 public void Subtract (ICollection some_uris)
144 lock (this) {
145 if (cancelled)
146 return;
148 Debug.Assert (workers > 0, "Subtracting Hits from idle QueryResult");
150 if (some_uris.Count == 0)
151 return;
153 if (IsIndexListener) {
154 if (HitsSubtractedEvent != null)
155 HitsSubtractedEvent (this, some_uris);
156 return;
159 ArrayList filtered_uris = new ArrayList ();
161 // We only get to subtract a URI if it was previously added.
162 foreach (Uri uri in some_uris) {
163 if (uri != null && uri_hash.Contains (uri)) {
164 filtered_uris.Add (uri);
165 uri_hash.Remove (uri);
169 if (HitsSubtractedEvent != null && filtered_uris.Count > 0)
170 HitsSubtractedEvent (this, filtered_uris);
174 //////////////////////////////////
176 // Given the Uri of a Hit contained in the QueryResult, return that Hit.
177 public Hit GetHitFromUri (Uri uri)
179 return uri_hash [uri] as Hit;
182 public ICollection HitUris {
183 get { return uri_hash.Keys; }
186 //////////////////////////////////////////////////////////////////////////////////////
188 class QueryWorkerClosure {
189 IQueryWorker worker;
190 QueryResult result;
192 public QueryWorkerClosure (IQueryWorker _worker, QueryResult _result)
194 worker = _worker;
195 result = _result;
198 public void Start ()
200 try {
201 worker.DoWork ();
202 } catch (Exception e) {
203 Logger.Log.Error (e, "QueryWorker '{0}' threw an exception", worker);
205 try {
206 result.WorkerFinished (worker);
207 } catch (Exception e) {
208 Logger.Log.Error ("QueryResult threw an exception while calling WorkerFinished for '{0}'",
209 worker);
214 public void AttachWorker (IQueryWorker worker)
216 lock (this) {
217 if (cancelled)
218 return;
220 QueryWorkerClosure qwc;
221 qwc = new QueryWorkerClosure (worker, this);
223 // QueryDriver has an enclosing WorkerStart,
224 // so if we call WorkerStart in this thread,
225 // all the workers will have a chance
226 // to start before Finished is called
228 if (!WorkerStartNoLock (worker))
229 return;
231 ExceptionHandlingThread.Start (new ThreadStart (qwc.Start));
235 private bool WorkerStartNoLock (object o)
237 if (!Shutdown.WorkerStart (o))
238 return false;
240 DateTime now = DateTime.Now;
241 per_worker_started_time [o] = now;
242 ++workers;
243 if (workers == 1) {
244 started_time = now;
245 if (StartedEvent != null)
246 StartedEvent (this);
250 return true;
254 internal bool WorkerStart (object o)
256 lock (this) {
257 return WorkerStartNoLock (o);
261 internal void WorkerFinished (object o)
263 lock (this) {
264 Debug.Assert (workers > 0, "Too many calls to WorkerFinished");
265 --workers;
268 DateTime now = DateTime.Now;
270 //DateTime then = (DateTime) per_worker_started_time [o];
271 //Logger.Log.Debug ("{0} finished in {1:0.00}s", o, (now - then).TotalSeconds);
273 if (workers == 0) {
274 finished_time = now;
275 //Logger.Log.Debug ("Last worker finished {0:0.00}s after start",
276 //(finished_time - started_time).TotalSeconds);
277 if (FinishedEvent != null)
278 FinishedEvent (this);
279 Monitor.Pulse (this);
282 Shutdown.WorkerFinished (o);
285 public void Wait ()
287 lock (this) {
288 while (true) {
289 if (cancelled || workers == 0)
290 return;
291 Monitor.Wait (this);