2 // CalendarQueryable.cs
4 // Copyright (C) 2004 Novell, Inc.
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
.Collections
;
31 using System
.Threading
;
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 ();
59 int foundCount
= Watch (cal_dir
);
61 Log
.Info ("Found {0} calendars in {1}", foundCount
, timer
);
64 public override void Start ()
68 ExceptionHandlingThread
.Start (new ThreadStart (StartWorker
));
71 private int Watch (string path
)
73 DirectoryInfo root
= new DirectoryInfo (path
);
79 Queue queue
= new Queue ();
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
);
90 foreach (FileInfo file
in dir
.GetFiles ()) {
91 IndexCalendar (file
.FullName
, Scheduler
.Priority
.Generator
);
95 foreach (DirectoryInfo subdir
in dir
.GetDirectories ())
96 queue
.Enqueue (subdir
);
102 private void OnInotifyEvent (int wd
,
106 Inotify
.EventType type
)
108 if (subitem
== "" || ! watched
.Contains (wd
))
111 string full_path
= Path
.Combine (path
, subitem
);
113 Console
.WriteLine ("{0}: {1}", type
, full_path
);
117 case Inotify
.EventType
.CreateSubdir
:
121 case Inotify
.EventType
.Modify
:
122 IndexCalendar (full_path
, Scheduler
.Priority
.Immediate
);
127 private void IndexCalendar (string filename
, Scheduler
.Priority priority
)
129 FileInfo info
= new FileInfo (filename
);
130 if (! info
.Exists
|| Driver
.IsUpToDate (filename
))
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
);
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
;
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);
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
));
186 TimeSpan utc_offset
= DateTime
.Now
- DateTime
.UtcNow
;
191 date
= new DateTime (Convert
.ToInt32 (year_str
),
192 Convert
.ToInt32 (month_str
),
193 Convert
.ToInt32 (day_str
));
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
);
223 if (t
.TokenText
.ToLower () == "vevent") {
224 this.indexables
.Add (this.cur
);
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")
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)
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)
296 switch (this.cur_id
.ToLower ()) {
298 // When the event starts; in local timezone.
299 this.cur
.AddProperty (Property
.NewDate ("fixme:starttime", ParseICalDate (t
.TokenText
)));
303 // When the event starts; in local timezone.
304 this.cur
.AddProperty (Property
.NewDate ("fixme:endtime", ParseICalDate (t
.TokenText
)));
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)
321 switch (this.cur_id
.ToLower ()) {
323 this.cur
.Uri
= new Uri ("calendar:///" + t
.TokenText
);
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
331 this.cur
.AddProperty (Property
.NewDate ("fixme:starttime", ParseICalDate (t
.TokenText
)));
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
)));
342 this.cur
.Timestamp
= ParseICalDate (t
.TokenText
, true);
346 // Short summary of the event
347 this.cur
.AddProperty (Property
.New ("fixme:summary", t
.TokenText
));
351 // Longer description of the event
352 StringReader reader
= new StringReader (t
.TokenText
);
353 this.cur
.SetTextReader (reader
);
357 // Where the event takes place
358 this.cur
.AddProperty (Property
.NewKeyword ("fixme:location", t
.TokenText
));
362 // Categories associated with this event
363 this.cur
.AddProperty (Property
.NewKeyword ("fixme:categories", t
.TokenText
));
367 // private, public, or confidential
368 this.cur
.AddProperty (Property
.NewKeyword ("fixme:class", t
.TokenText
));
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
);