Dont reindex already indexed files. Yet another bug uncovered by the DateTime fixes.
[beagle.git] / BeagleClient / Hit.cs
blob8abdc4205f5f340ea558c83461cba526eef63231
1 //
2 // Hit.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.IO;
32 using System.Xml;
33 using System.Xml.Serialization;
35 using Beagle.Util;
37 namespace Beagle {
39 public class Hit: Versioned, IComparable {
41 // A URI we can use to locate the source of this match.
42 private Uri uri = null;
44 // A URI of this Hit's container element
45 private Uri parent_uri = null;
47 // File, Web, MailMessage, IMLog, etc.
48 private string type = null;
50 // If applicable, otherwise set to null.
51 private string mimeType = null;
53 // IndexUser, IndexSystem, Google, Addressbook, iFolder, etc.
54 private string source = null;
56 // This is used to hold a copy of the Queryable in the
57 // server-side copy of the Hit. It is always null
58 // on the client-side.
59 private object sourceObject = null;
61 // High scores imply greater relevance.
62 private double score = 0.0;
64 private ArrayList properties = new ArrayList ();
66 private enum SpecialType {
67 Unknown,
68 None,
69 Invalid,
70 File,
71 Directory
74 SpecialType special = SpecialType.Unknown;
76 private string path;
77 private FileInfo fileInfo = null;
78 private DirectoryInfo directoryInfo = null;
80 //////////////////////////
82 [XmlIgnore]
83 public Uri Uri {
84 get { return uri; }
85 set { uri = value; }
88 [XmlAttribute ("Uri")]
89 public string EscapedUri {
90 get {
91 return UriFu.UriToEscapedString (uri);
94 set {
95 uri = UriFu.EscapedStringToUri (value);
99 [XmlIgnore]
100 public Uri ParentUri {
101 get { return parent_uri; }
102 set { parent_uri = value; }
105 [XmlAttribute ("ParentUri")]
106 public string EscapedParentUri {
107 get {
108 if (parent_uri == null)
109 return null;
111 return UriFu.UriToEscapedString (parent_uri);
114 set {
115 if (value == null)
116 parent_uri = null;
117 else
118 parent_uri = UriFu.EscapedStringToUri (value);
122 // DEPRECATED: This is now stored as a property.
123 [XmlAttribute]
124 public string Type {
125 get { return type; }
126 set { type = value; }
129 // DEPRECATED: This is now stored as a property.
130 [XmlAttribute]
131 public string MimeType {
132 get { return mimeType; }
133 set { mimeType = value; }
136 [XmlAttribute]
137 public string Source {
138 get { return source; }
139 set { source = value; }
142 [XmlIgnore]
143 public object SourceObject {
144 get { return sourceObject; }
145 set { sourceObject = value; }
148 [XmlAttribute]
149 public double Score {
150 get { return score; }
151 set { score = value; }
154 //////////////////////////
156 private void SpecialHandling ()
158 if (special != SpecialType.Unknown)
159 return;
161 if (uri.IsFile) {
162 path = uri.LocalPath;
163 if (File.Exists (path))
164 special = SpecialType.File;
165 else if (Directory.Exists (path))
166 special = SpecialType.Directory;
167 else
168 special = SpecialType.Invalid;
171 if (special == SpecialType.Unknown)
172 special = SpecialType.None;
175 public bool IsValid {
176 get { SpecialHandling (); return special != SpecialType.Invalid; }
179 public bool IsFile {
180 get { SpecialHandling (); return special == SpecialType.File; }
183 public bool IsDirectory {
184 get { SpecialHandling (); return special == SpecialType.Directory; }
187 public bool IsFileSystem {
188 get { return IsFile || IsDirectory; }
191 public string Path {
192 get { SpecialHandling (); return path; }
195 public string PathQuoted {
196 get { return Path.Replace (" ", "\\ "); }
199 public string FileName {
200 get { return Path != null ? System.IO.Path.GetFileName (Path) : null; }
203 public string DirectoryName {
204 get { return Path != null ? System.IO.Path.GetDirectoryName (Path) : null; }
207 [XmlIgnore]
208 public FileSystemInfo FileSystemInfo {
209 get {
210 if (IsFile)
211 return (FileSystemInfo) FileInfo;
212 else if (IsDirectory)
213 return (FileSystemInfo) DirectoryInfo;
214 else
215 return null;
219 [XmlIgnore]
220 public FileInfo FileInfo {
221 get {
222 if (fileInfo == null && IsFile)
223 fileInfo = new FileInfo (Path);
224 return fileInfo;
228 [XmlIgnore]
229 public DirectoryInfo DirectoryInfo {
230 get {
231 if (directoryInfo == null && IsDirectory)
232 directoryInfo = new DirectoryInfo (Path);
233 return directoryInfo;
237 //////////////////////////
239 [XmlArray]
240 [XmlArrayItem (ElementName="Property", Type=typeof (Property))]
241 public ArrayList Properties {
242 get { return properties; }
245 public void AddProperty (Property prop)
247 int loc = properties.BinarySearch (prop);
249 // If the value is not in the array we get its position
250 // by taking bitwise complement.
251 if (loc < 0)
252 loc = ~loc;
254 properties.Insert (loc, prop);
257 public void AddProperty (ICollection props)
259 properties.AddRange (props);
260 properties.Sort ();
264 private bool FindProperty (string key, out int first, out int top)
267 first = 0;
268 top = 0;
270 int range = properties.Count - 1;
271 if (range < 0)
272 return false;
274 Property prop;
275 // O(log n + |range|)-time algorithm for 1-d range query
276 while (range > 1) {
277 // find middle index
278 int mid = first + (range/2);
280 prop = properties [mid] as Property;
281 // Properties are sorted first by Key, then by Value
282 // We only need to compare the key of the middle element to the key in parameter
283 if (String.Compare (key, prop.Key) > 0) {
284 // minimum item in right subtree is smaller
285 // move right
286 first = mid;
287 range -= (range/2);
288 } else
289 // move left
290 range = (range/2);
293 // first points to
294 // - either the previous item in the BST
295 // - or the next to the previous item in the BST
296 prop = (Property) properties [first];
297 if (prop.Key != key)
298 first = first + 1;
299 if (first >= properties.Count)
300 return false;
301 prop = (Property) properties [first];
302 if (prop.Key != key)
303 return false;
305 top = first + 1;
306 // Since range will be small, do a linear scan from here.
307 // We could do another BST traversal at the cost of O(log-n),
308 // but its not worth it.
309 while (top < properties.Count) {
310 prop = properties [top] as Property;
311 if (prop.Key != key)
312 break;
313 ++top;
316 return true;
319 public string this [string key] {
320 get {
321 int first, top;
322 if (! FindProperty (key, out first, out top))
323 return null;
325 if (top - first != 1) {
326 Logger.Log.Warn ("Accessed multi-property key with Hit's indexer.");
327 return null;
330 Property prop;
331 prop = properties [first] as Property;
332 return prop.Value;
335 set {
336 int first = 0, top = 0;
338 // If we've never heard of this property, add it.
339 if (! FindProperty (key, out first, out top)) {
340 AddProperty (Property.New (key, value));
341 return;
344 // If it has appeared once before, clobber the existing
345 // value. This emulates the previous (broken) semantics.
347 if (top - first == 1) {
348 properties [first] = Property.New (key, value);
349 return;
352 // Otherwise throw an exception (which sort of sucks,
353 // but we don't really know what to do there)
354 throw new Exception (String.Format ("Attempt to re-set multi-property '{0}' via the indexer", key));
358 public string GetFirstProperty (string key)
360 int first, top;
361 if (! FindProperty (key, out first, out top))
362 return null;
363 Property prop;
364 prop = properties [first] as Property;
365 return prop.Value;
368 public string[] GetProperties (string key)
370 int first, top;
371 if (! FindProperty (key, out first, out top))
372 return null;
374 string[] values = new string [top - first];
376 for (int i = 0; first + i < top; i++) {
377 Property prop = properties [first + i] as Property;
378 values [i] = prop.Value;
381 return values;
384 //////////////////////////
386 public override int GetHashCode ()
388 return (uri != null ? uri.ToString().GetHashCode () : 0)
389 ^ (type != null ? type.GetHashCode () : 0)
390 ^ (source != null ? source.GetHashCode () : 0);
393 //////////////////////////
395 public int CompareTo (object obj)
397 Hit otherHit = (Hit) obj;
398 // Notice that we sort by time from most to least recent
399 return DateTime.Compare (otherHit.Timestamp, this.Timestamp);