Remove debug spew
[beagle.git] / beagled / KonqBookmarkQueryable / KonqBookmarkQueryable.cs
blob48e528c3b913bf1dc8a20168460a6a1d4787ace1
1 //
2 // KonqBookmarkQueryable.cs
3 //
4 // Copyright (C) 2006 Debajyoti Bera
5 //
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a
8 // copy of this software and associated documentation files (the "Software"),
9 // to deal in the Software without restriction, including without limitation
10 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 // and/or sell copies of the Software, and to permit persons to whom the
12 // Software is furnished to do so, subject to the following conditions:
14 // The above copyright notice and this permission notice shall be included in
15 // all copies or substantial portions of the Software.
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 // DEALINGS IN THE SOFTWARE.
26 using System;
27 using System.IO;
28 using System.Collections;
29 using System.Threading;
30 using System.Text;
31 using System.Xml;
32 using System.Xml.Serialization;
33 using System.Globalization;
35 using Beagle.Daemon;
36 using Beagle.Util;
38 namespace Beagle.Daemon.KBookmarkQueryable {
40 [QueryableFlavor (Name="KonqBookmark", Domain=QueryDomain.Local, RequireInotify=false)]
41 public class KonqBookmarkQueryable : LuceneFileQueryable {
43 private string konq_dir;
44 private string bookmark_file;
45 private Hashtable last_modified_table;
47 // construct a serializer and keep it handy for indexablegenerator to use
48 private XmlSerializer serializer = null;
49 public XmlSerializer Serializer {
50 get {
51 if (serializer == null)
52 serializer = new XmlSerializer (typeof (Bookmark));
53 return serializer;
57 // 1: Store URL as text
58 const int MINOR_VERSION = 1;
60 public KonqBookmarkQueryable () : base ("KonqBookmarkIndex", MINOR_VERSION)
62 konq_dir = Path.Combine (PathFinder.HomeDir, ".kde");
63 konq_dir = Path.Combine (konq_dir, "share");
64 konq_dir = Path.Combine (konq_dir, "apps");
65 konq_dir = Path.Combine (konq_dir, "konqueror");
67 bookmark_file = Path.Combine (konq_dir, "bookmarks.xml");
69 last_modified_table = new Hashtable ();
72 /////////////////////////////////////////////////
74 public override void Start ()
76 base.Start ();
78 ExceptionHandlingThread.Start (new ThreadStart (StartWorker));
81 private void StartWorker ()
83 if (!Directory.Exists (konq_dir)) {
84 GLib.Timeout.Add (60000, new GLib.TimeoutHandler (CheckForExistence));
85 return;
88 if (Inotify.Enabled) {
89 Inotify.EventType mask = Inotify.EventType.CloseWrite
90 | Inotify.EventType.MovedTo;
92 Inotify.Subscribe (konq_dir, OnInotifyEvent, mask);
93 } else {
94 FileSystemWatcher fsw = new FileSystemWatcher ();
95 fsw.Path = konq_dir;
96 fsw.Filter = bookmark_file;
98 fsw.Changed += new FileSystemEventHandler (OnChanged);
99 fsw.Created += new FileSystemEventHandler (OnChanged);
100 fsw.Renamed += new RenamedEventHandler (OnChanged);
102 fsw.EnableRaisingEvents = true;
105 if (File.Exists (bookmark_file)) {
106 if (! FileAttributesStore.IsUpToDate (bookmark_file))
107 Index ();
108 else
109 ScanBookmarkInitial ();
113 private bool CheckForExistence ()
115 if (!Directory.Exists (konq_dir))
116 return true;
118 this.Start ();
120 return false;
123 // We need to scan the bookmark file nitially to fill up last_modified_table
124 // Otherwise, we might miss deletions that occur before any addition
125 private void ScanBookmarkInitial ()
127 BookmarkIndexableGenerator generator = new BookmarkIndexableGenerator (this, bookmark_file, last_modified_table, true);
129 // just a dummy scan
130 while (generator.HasNextIndexable ())
131 generator.GetNextIndexable ();
134 /////////////////////////////////////////////////
136 // Modified/Created event using Inotify
138 private void OnInotifyEvent (Inotify.Watch watch,
139 string path,
140 string subitem,
141 string srcpath,
142 Inotify.EventType type)
144 if (Path.Combine (path, subitem) != bookmark_file)
145 return;
147 Index ();
150 // Modified/Created event using FSW
152 private void OnChanged (object o, FileSystemEventArgs args)
154 Index ();
157 /////////////////////////////////////////////////
159 private void Index ()
161 if (ThisScheduler.ContainsByTag ("KonqBookmark")) {
162 Log.Debug ("Not adding task for already running KonqBookmark task");
163 return;
166 BookmarkIndexableGenerator generator = new BookmarkIndexableGenerator (this, bookmark_file, last_modified_table, false);
167 Scheduler.Task task;
168 task = NewAddTask (generator);
169 task.Tag = "KonqBookmark";
170 task.Priority = Scheduler.Priority.Delayed;
171 task.SubPriority = 0;
172 ThisScheduler.Add (task);
175 internal void RemoveDeletedBookmarks (Hashtable deleted_bookmarks)
177 ArrayList to_delete = new ArrayList ();
178 lock (last_modified_table) {
179 foreach (string uid in last_modified_table.Keys) {
180 if (! deleted_bookmarks.Contains (uid) ||
181 (bool)deleted_bookmarks [uid] == true) {
182 to_delete.Add (uid);
187 foreach (string uid in to_delete) {
188 RemoveBookmark (uid);
189 last_modified_table.Remove (uid);
193 private void RemoveBookmark (string uid)
195 Uri uri = new Uri (uid);
196 Log.Debug ("Removing contact {0}", uri);
197 Scheduler.Task task = NewRemoveTask (uri);
198 task.Priority = Scheduler.Priority.Immediate;
199 task.SubPriority = 0;
200 ThisScheduler.Add (task);
206 * Indexable generator for Konqueror Bookmarks
208 public class BookmarkIndexableGenerator : IIndexableGenerator {
209 private string bookmark_file;
210 private KonqBookmarkQueryable queryable;
211 private XmlTextReader reader;
212 private bool is_valid_file = true;
213 private bool initial_scan = false;
214 private Hashtable last_modified_table;
215 private Hashtable deleted_bookmarks;
216 private DateTime file_last_write_time;
218 private ArrayList folder_stack; // simulate a stack
219 private string current_folder = String.Empty;
220 private string current_title = String.Empty;
221 private string current_folder_title = String.Empty;
222 private DateTime current_dt;
224 private string current_bookmark_id = String.Empty;
225 private Bookmark current_bookmark;
226 private XmlSerializer serializer;
228 public BookmarkIndexableGenerator (KonqBookmarkQueryable queryable,
229 string bookmark_file,
230 Hashtable last_modified_table,
231 bool initial_scan)
233 this.queryable = queryable;
234 this.bookmark_file = bookmark_file;
235 this.initial_scan = initial_scan;
237 ReadBookmarkHeader ();
238 if (! is_valid_file)
239 return;
241 this.serializer = queryable.Serializer;
242 this.last_modified_table = last_modified_table;
243 this.folder_stack = new ArrayList ();
245 lock (last_modified_table) {
246 this.deleted_bookmarks = new Hashtable (last_modified_table.Count);
247 foreach (string bookmark_path in last_modified_table.Keys)
248 this.deleted_bookmarks [bookmark_path] = true;
251 // cache the last write time
252 file_last_write_time = FileSystem.GetLastWriteTimeUtc (bookmark_file);
255 public void PostFlushHook ()
259 public string StatusName {
260 get { return current_bookmark_id; }
263 private bool IsUpToDate (string path)
265 return queryable.FileAttributesStore.IsUpToDate (path);
268 private void ReadBookmarkHeader () {
269 if ( (! initial_scan) && IsUpToDate (bookmark_file)) {
270 is_valid_file = false;
271 return;
273 try {
274 Log.Debug ("Opening bookmark file: {0}", bookmark_file);
275 reader = new XmlTextReader (bookmark_file);
276 reader.WhitespaceHandling = WhitespaceHandling.None;
278 is_valid_file = true;
280 // move to beginning of document
281 reader.MoveToContent();
282 // move to <xbel> node
283 reader.ReadStartElement ("xbel");
284 } catch (XmlException ex) {
285 Logger.Log.Warn (ex, "Caught exception parsing bookmark file:");
286 is_valid_file = false;
287 reader.Close ();
291 public bool HasNextIndexable ()
293 if (!is_valid_file || reader == null)
294 return false;
296 current_bookmark = null;
297 string bookmark_string = "";
298 try {
299 while (! reader.EOF) {
300 if (ReadNextBookmark ())
301 return true;
303 } catch (XmlException) {
304 // probably no more <item>
307 is_valid_file = false;
308 reader.Close ();
309 if (! initial_scan)
310 queryable.RemoveDeletedBookmarks (deleted_bookmarks);
311 return false;
314 private bool ReadNextBookmark ()
316 while (reader.NodeType == XmlNodeType.EndElement) {
317 if (reader.Name == "folder") {
318 folder_stack.RemoveAt (folder_stack.Count - 1);
319 ResetCurrentFolder ();
320 //Log.Debug ("Resetting folder to [{0}]", current_folder);
322 reader.Read ();
325 while (!reader.EOF && reader.NodeType == XmlNodeType.Element) {
326 string elementname = reader.Name;
327 if (elementname == "title") {
328 string title = reader.ReadString ();
329 current_folder_title = title;
330 folder_stack.Add (title);
331 ResetCurrentFolder ();
332 reader.ReadEndElement ();
333 continue;
334 } else if (elementname == "bookmark") {
335 string bookmark_string = reader.ReadOuterXml ();
336 current_bookmark = (Bookmark) serializer.Deserialize (new StringReader (bookmark_string));
337 if (SetLastModifiedTable ())
338 return true;
339 } else {
340 reader.Read ();
344 return false;
347 private bool SetLastModifiedTable ()
349 current_bookmark_id = String.Format ("kbookmark:///{0};title={1}", current_folder, current_bookmark.Title);
351 if (current_bookmark.Info != null &&
352 current_bookmark.Info.Metadata != null &&
353 current_bookmark.Info.Metadata.TimeLastVisited != 0) {
354 DateTime date = DateTimeUtil.UnixToDateTimeUtc (0);
355 current_dt = date.AddSeconds (current_bookmark.Info.Metadata.TimeLastVisited);
356 } else
357 current_dt = file_last_write_time;
359 // Mark bookmark seen ('undeleted')
360 deleted_bookmarks [current_bookmark_id] = false;
362 lock (last_modified_table) {
363 if (last_modified_table.Contains (current_bookmark_id)) {
364 DateTime old_dt = (DateTime) last_modified_table [current_bookmark_id];
365 // Address up-to-date
366 if (current_dt == old_dt) {
367 //Log.Debug ("Datetime unchanged [{0}] = {1}", current_bookmark_id, current_dt);
368 return false;
369 } else {
370 //Log.Debug ("Updating last_mod_date [{0}] = {1}", current_bookmark_id, current_dt);
371 last_modified_table [current_bookmark_id] = current_dt;
373 } else {
374 //Log.Debug ("Adding last_mod_date [{0}] = {1}", current_bookmark_id, current_dt);
375 last_modified_table [current_bookmark_id] = current_dt;
378 return true;
381 private void ResetCurrentFolder ()
383 // FIXME: Joining using '/' isn't safe since
384 // Konqueror allows '/' in the path name
385 // But we need to create a unique URI from the folder path
386 // and href
387 StringBuilder sb = new StringBuilder ();
388 foreach (string folder in folder_stack) {
389 sb.Append (folder);
390 sb.Append ('/');
392 current_folder = sb.ToString ();
395 public Indexable GetNextIndexable ()
397 if (current_bookmark != null)
398 return BookmarkToIndexable ();
400 return null;
403 private Indexable BookmarkToIndexable ()
405 Indexable indexable = new Indexable (new Uri (current_bookmark_id));
406 indexable.ParentUri = UriFu.PathToFileUri (bookmark_file);
407 indexable.HitType = "Bookmark";
408 indexable.Timestamp = current_dt;
409 indexable.NoContent = true;
411 indexable.AddProperty (Property.New ("dc:title", current_bookmark.Title));
412 indexable.AddProperty (Property.New ("dc:identifier", current_bookmark.Href));
413 indexable.AddProperty (Property.NewUnsearched ("fixme:icon", current_bookmark.Icon));
414 if (current_bookmark.Info != null &&
415 current_bookmark.Info.Metadata != null &&
416 current_bookmark.Info.Metadata.NumVisited != 0)
417 indexable.AddProperty (Property.NewUnsearched ("fixme:visit_count", current_bookmark.Info.Metadata.NumVisited));
418 foreach (string folder in folder_stack)
419 indexable.AddProperty (Property.New ("fixme:folder", folder));
421 return indexable;
426 // we will deserialize XML fragments, so there wont be any <? xml ... ?>
427 [System.Xml.Serialization.XmlRoot("bookmark", Namespace="", IsNullable=false)]
428 [System.Xml.Serialization.XmlType("bookmark", Namespace="")]
429 public class Bookmark {
430 [XmlAttribute ("icon")] public string Icon = "";
431 [XmlAttribute ("href")] public string Href = "";
432 [XmlElement ("title")] public string Title;
433 [XmlElement ("info")] public Info Info;
436 public class Info {
437 [XmlElement ("metadata")] public Metadata Metadata;
440 public class Metadata {
441 [XmlElement ("time_added")] public long TimeAdded = 0;
442 [XmlElement ("time_visited")] public long TimeLastVisited = 0;
443 [XmlElement ("visit_count")] public int NumVisited = 0;