More patterns to remove.
[beagle.git] / beagled / CalendarQueryable / CalendarQueryable.cs
blobf8e2cabb499c6510fa9699ddbaa274d02bac6af6
1 //
2 // CalendarQueryable.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.
27 using System;
28 using System.Collections;
29 using System.IO;
30 using System.Text;
31 using System.Threading;
33 using Beagle.Daemon;
34 using Beagle.Util;
36 using ICalParser = Semaview.Shared.ICalParser;
38 namespace Beagle.Daemon.CalendarQueryable {
40 [QueryableFlavor (Name="Calendar", Domain=QueryDomain.Local)]
41 public class CalendarQueryable : LuceneQueryable {
43 public static Logger Log = Logger.Get ("calendar");
44 private string cal_dir;
46 Hashtable watched = new Hashtable ();
48 public CalendarQueryable () : base ("CalendarIndex")
50 cal_dir = Path.Combine (PathFinder.HomeDir, ".evolution/calendar/local");
53 private void StartWorker ()
55 Inotify.Event += OnInotifyEvent;
57 Stopwatch timer = new Stopwatch ();
58 timer.Start ();
59 int foundCount = Watch (cal_dir);
60 timer.Stop ();
61 Log.Info ("Found {0} calendars in {1}", foundCount, timer);
64 public override void Start ()
66 base.Start ();
68 ExceptionHandlingThread.Start (new ThreadStart (StartWorker));
71 private int Watch (string path)
73 DirectoryInfo root = new DirectoryInfo (path);
74 if (! root.Exists)
75 return 0;
77 int file_count = 0;
79 Queue queue = new Queue ();
80 queue.Enqueue (root);
82 while (queue.Count > 0) {
83 DirectoryInfo dir = queue.Dequeue () as DirectoryInfo;
85 int wd = Inotify.Watch (dir.FullName,
86 Inotify.EventType.CreateSubdir
87 | Inotify.EventType.Modify);
88 watched [wd] = true;
90 foreach (FileInfo file in dir.GetFiles ()) {
91 IndexCalendar (file.FullName, Scheduler.Priority.Generator);
92 ++file_count;
95 foreach (DirectoryInfo subdir in dir.GetDirectories ())
96 queue.Enqueue (subdir);
99 return file_count;
102 private void OnInotifyEvent (int wd,
103 string path,
104 string subitem,
105 string srcpath,
106 Inotify.EventType type)
108 if (subitem == "" || ! watched.Contains (wd))
109 return;
111 string full_path = Path.Combine (path, subitem);
113 Console.WriteLine ("{0}: {1}", type, full_path);
115 switch (type) {
117 case Inotify.EventType.CreateSubdir:
118 Watch (full_path);
119 break;
121 case Inotify.EventType.Modify:
122 IndexCalendar (full_path, Scheduler.Priority.Immediate);
123 break;
127 private void IndexCalendar (string filename, Scheduler.Priority priority)
129 FileInfo info = new FileInfo (filename);
130 if (! info.Exists || Driver.IsUpToDate (filename))
131 return;
133 Scheduler.TaskGroup group;
134 group = NewMarkingTaskGroup (filename, info.LastWriteTime);
136 IndexableEmitter emitter = new IndexableEmitter ();
137 ICalParser.Parser parser = new ICalParser.Parser (new StreamReader (info.FullName), emitter);
138 parser.Parse ();
140 if (!parser.HasErrors) {
141 foreach (Indexable indexable in emitter.Indexables) {
142 Scheduler.Task task = NewAddTask (indexable);
143 task.Priority = priority;
144 task.SubPriority = 0;
145 task.AddTaskGroup (group);
146 ThisScheduler.Add (task);
152 class IndexableEmitter : ICalParser.IEmitter {
153 private ArrayList indexables = new ArrayList ();
154 private ICalParser.Parser parser;
156 // Our current state
157 private Indexable cur = null;
158 private string cur_id = null;
160 public ICollection Indexables {
161 get { return this.indexables; }
164 private static DateTime ParseICalDate (string icaldate, bool utc)
166 // There is no error checking at all.
167 string year_str = icaldate.Substring (0, 4);
168 string month_str = icaldate.Substring (4, 2);
169 string day_str = icaldate.Substring (6, 2);
171 DateTime date;
173 if (icaldate.Length >= 15) {
174 string hour_str = icaldate.Substring (9, 2);
175 string minute_str = icaldate.Substring (11, 2);
176 string second_str = icaldate.Substring (13, 2);
178 date = new DateTime (Convert.ToInt32 (year_str),
179 Convert.ToInt32 (month_str),
180 Convert.ToInt32 (day_str),
181 Convert.ToInt32 (hour_str),
182 Convert.ToInt32 (minute_str),
183 Convert.ToInt32 (second_str));
185 if (utc) {
186 TimeSpan utc_offset = DateTime.Now - DateTime.UtcNow;
188 date += utc_offset;
190 } else {
191 date = new DateTime (Convert.ToInt32 (year_str),
192 Convert.ToInt32 (month_str),
193 Convert.ToInt32 (day_str));
196 return date;
199 private static DateTime ParseICalDate (string icaldate)
201 bool utc = icaldate.EndsWith ("Z");
203 return ParseICalDate (icaldate, utc);
206 // Implement IEmitter
207 public void doIntro ()
209 CalendarQueryable.Log.Debug ("-------");
212 public void doOutro ()
214 CalendarQueryable.Log.Debug ("-------");
217 public void doEnd (ICalParser.Token t)
219 CalendarQueryable.Log.Debug ("doEnd: {0}", t.TokenText);
221 this.cur_id = null;
223 if (t.TokenText.ToLower () == "vevent") {
224 this.indexables.Add (this.cur);
225 this.cur = null;
229 public void doResourceBegin (ICalParser.Token t)
231 CalendarQueryable.Log.Debug ("doResourceBegin: {0}", t.TokenText);
234 public void doBegin (ICalParser.Token t)
236 CalendarQueryable.Log.Debug ("doBegin: {0}", t.TokenText);
239 public void doComponentBegin (ICalParser.Token t)
241 CalendarQueryable.Log.Debug ("doComponentBegin: {0}", t.TokenText);
243 // FIXME: Need more types to index.
244 if (t.TokenText.ToLower () != "vevent")
245 return;
247 this.cur = new Indexable ();
248 this.cur.Type = "Calendar";
251 public void doComponent ()
255 public void doEndComponent ()
259 public void doID (ICalParser.Token t)
261 CalendarQueryable.Log.Debug ("doID: {0}", t.TokenText);
263 if (this.cur == null)
264 return;
266 this.cur_id = t.TokenText;
269 public void doSymbolic (ICalParser.Token t)
271 CalendarQueryable.Log.Debug ("doSymbolic: {0}", t.TokenText);
274 public void doResource (ICalParser.Token t)
276 CalendarQueryable.Log.Debug ("doResource: {0}", t.TokenText);
279 public void doURIResource (ICalParser.Token t)
281 CalendarQueryable.Log.Debug ("doURIResource: {0}", t.TokenText);
284 public void doMailto (ICalParser.Token t)
286 CalendarQueryable.Log.Debug ("doMailto: {0}", t.TokenText);
289 public void doValueProperty (ICalParser.Token t, ICalParser.Token iprop)
291 CalendarQueryable.Log.Debug ("doValueProperty: {0} {1}", t.TokenText, iprop == null ? "(null)" : iprop.TokenText);
293 if (this.cur == null || this.cur_id == null)
294 return;
296 switch (this.cur_id.ToLower ()) {
297 case "dtstart":
298 // When the event starts; in local timezone.
299 this.cur.AddProperty (Property.NewDate ("fixme:starttime", ParseICalDate (t.TokenText)));
300 break;
302 case "dtend":
303 // When the event starts; in local timezone.
304 this.cur.AddProperty (Property.NewDate ("fixme:endtime", ParseICalDate (t.TokenText)));
305 break;
309 public void doIprop (ICalParser.Token t, ICalParser.Token iprop)
311 CalendarQueryable.Log.Debug ("doIprop: {0} {1}", t.TokenText, iprop.TokenText);
314 public void doRest (ICalParser.Token t, ICalParser.Token id)
316 CalendarQueryable.Log.Debug ("doRest: {0} {1}", t.TokenText, id.TokenText);
318 if (this.cur == null || this.cur_id == null)
319 return;
321 switch (this.cur_id.ToLower ()) {
322 case "uid":
323 this.cur.Uri = new Uri ("calendar:///" + t.TokenText);
324 break;
326 case "dtstart":
327 // When the event starts; in local timezone.
328 // Usually this won't be processed here, it'll
329 // more likely be in doValueProperty w/ a
330 // timezone.
331 this.cur.AddProperty (Property.NewDate ("fixme:starttime", ParseICalDate (t.TokenText)));
332 break;
334 case "dtend":
335 // When the event ends; in local timezone.
336 // Same deal as dtstart above.
337 this.cur.AddProperty (Property.NewDate ("fixme:endtime", ParseICalDate (t.TokenText)));
338 break;
340 case "lastmodified":
341 // Always in GMT
342 this.cur.Timestamp = ParseICalDate (t.TokenText, true);
343 break;
345 case "summary":
346 // Short summary of the event
347 this.cur.AddProperty (Property.New ("fixme:summary", t.TokenText));
348 break;
350 case "description":
351 // Longer description of the event
352 StringReader reader = new StringReader (t.TokenText);
353 this.cur.SetTextReader (reader);
354 break;
356 case "location":
357 // Where the event takes place
358 this.cur.AddProperty (Property.NewKeyword ("fixme:location", t.TokenText));
359 break;
361 case "categories":
362 // Categories associated with this event
363 this.cur.AddProperty (Property.NewKeyword ("fixme:categories", t.TokenText));
364 break;
366 case "class":
367 // private, public, or confidential
368 this.cur.AddProperty (Property.NewKeyword ("fixme:class", t.TokenText));
369 break;
372 this.cur_id = null;
375 public void doAttribute (ICalParser.Token t1, ICalParser.Token t2)
377 CalendarQueryable.Log.Debug ("doAttribute: {0} {1}", t1.TokenText, t2.TokenText);
380 public ICalParser.Parser VParser {
381 get { return this.parser; }
382 set { this.parser = value; }
385 public void emit (string val)
387 CalendarQueryable.Log.Debug ("emit: {0}", val);