* Filters/FilterPackage.cs, Filters/FilterRPM.cs,
[beagle.git] / BeagleClient / Hit.cs
blob57517129c795824c300cc445d609dc2545224b23
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 ();
65 private bool sorted = true;
67 private enum SpecialType {
68 Unknown,
69 None,
70 Invalid,
71 File,
72 Directory
75 SpecialType special = SpecialType.Unknown;
77 private string path;
78 private FileInfo fileInfo = null;
79 private DirectoryInfo directoryInfo = null;
81 //////////////////////////
83 [XmlIgnore]
84 public Uri Uri {
85 get { return uri; }
86 set { uri = value; }
89 [XmlAttribute ("Uri")]
90 public string UriAsString {
91 get {
92 return UriFu.UriToSerializableString (uri);
95 set {
96 uri = UriFu.UriStringToUri (value);
100 [XmlIgnore]
101 public Uri ParentUri {
102 get { return parent_uri; }
103 set { parent_uri = value; }
106 [XmlAttribute ("ParentUri")]
107 public string ParentUriAsString {
108 get {
109 if (parent_uri == null)
110 return null;
112 return UriFu.UriToSerializableString (parent_uri);
115 set {
116 if (value == null)
117 parent_uri = null;
118 else
119 parent_uri = UriFu.UriStringToUri (value);
123 // DEPRECATED: This is now stored as a property.
124 [XmlAttribute]
125 public string Type {
126 get { return type; }
127 set { type = value; }
130 // DEPRECATED: This is now stored as a property.
131 [XmlAttribute]
132 public string MimeType {
133 get { return mimeType; }
134 set { mimeType = value; }
137 [XmlAttribute]
138 public string Source {
139 get { return source; }
140 set { source = value; }
143 [XmlIgnore]
144 public object SourceObject {
145 get { return sourceObject; }
146 set { sourceObject = value; }
149 [XmlAttribute]
150 public double Score {
151 get { return score; }
152 set { score = value; }
155 //////////////////////////
157 private void SpecialHandling ()
159 if (special != SpecialType.Unknown)
160 return;
162 if (uri.IsFile) {
163 path = uri.LocalPath;
164 if (File.Exists (path))
165 special = SpecialType.File;
166 else if (Directory.Exists (path))
167 special = SpecialType.Directory;
168 else
169 special = SpecialType.Invalid;
172 if (special == SpecialType.Unknown)
173 special = SpecialType.None;
176 public bool IsValid {
177 get { SpecialHandling (); return special != SpecialType.Invalid; }
180 public bool IsFile {
181 get { SpecialHandling (); return special == SpecialType.File; }
184 public bool IsDirectory {
185 get { SpecialHandling (); return special == SpecialType.Directory; }
188 public bool IsFileSystem {
189 get { return IsFile || IsDirectory; }
192 public string Path {
193 get { SpecialHandling (); return path; }
196 public string PathQuoted {
197 get { return Path.Replace (" ", "\\ "); }
200 public string FileName {
201 get { return Path != null ? System.IO.Path.GetFileName (Path) : null; }
204 public string DirectoryName {
205 get { return Path != null ? System.IO.Path.GetDirectoryName (Path) : null; }
208 [XmlIgnore]
209 public FileSystemInfo FileSystemInfo {
210 get {
211 if (IsFile)
212 return (FileSystemInfo) FileInfo;
213 else if (IsDirectory)
214 return (FileSystemInfo) DirectoryInfo;
215 else
216 return null;
220 [XmlIgnore]
221 public FileInfo FileInfo {
222 get {
223 if (fileInfo == null && IsFile)
224 fileInfo = new FileInfo (Path);
225 return fileInfo;
229 [XmlIgnore]
230 public DirectoryInfo DirectoryInfo {
231 get {
232 if (directoryInfo == null && IsDirectory)
233 directoryInfo = new DirectoryInfo (Path);
234 return directoryInfo;
238 //////////////////////////
240 [XmlArray]
241 [XmlArrayItem (ElementName="Property", Type=typeof (Property))]
242 public ArrayList Properties {
243 get { return properties; }
246 public void AddProperty (Property prop)
248 if (sorted && properties.Count > 0) {
249 Property last_prop;
250 last_prop = properties [properties.Count - 1] as Property;
251 if (last_prop.CompareTo (prop) > 0) // i.e. last_prop > prop
252 sorted = false;
255 properties.Add (prop);
258 private bool FindProperty (string key, out int first, out int top)
260 if (! sorted) {
261 properties.Sort ();
262 sorted = true;
265 first = 0;
266 top = 0;
267 int range = properties.Count - 1;
268 Property prop;
270 // O(log n + |range|)-time algorithm for 1-d range query
271 while (range > 1) {
272 // find middle index
273 int mid = first + (range/2);
275 prop = properties [mid] as Property;
276 // Properties are sorted first by Key, then by Value
277 // We only need to compare the key of the middle element to the key in parameter
278 if (String.Compare (key, prop.Key) > 0) {
279 // minimum item in right subtree is smaller
280 // move right
281 first = mid;
282 range -= (range/2);
283 } else
284 // move left
285 range = (range/2);
288 // first points to
289 // - either the previous item in the BST
290 // - or the next to the previous item in the BST
291 prop = (Property) properties [first];
292 if (prop.Key != key)
293 first = first + 1;
294 if (first >= properties.Count)
295 return false;
296 prop = (Property) properties [first];
297 if (prop.Key != key)
298 return false;
300 top = first + 1;
301 // Since range will be small, do a linear scan from here.
302 // We could do another BST traversal at the cost of O(log-n),
303 // but its not worth it.
304 while (top < properties.Count) {
305 prop = properties [top] as Property;
306 if (prop.Key != key)
307 break;
308 ++top;
311 return true;
314 public string this [string key] {
315 get {
316 int first, top;
317 if (! FindProperty (key, out first, out top))
318 return null;
320 if (top - first != 1) {
321 Logger.Log.Warn ("Accessed multi-property key with Hit's indexer.");
322 return null;
325 Property prop;
326 prop = properties [first] as Property;
327 return prop.Value;
330 set {
331 int first = 0, top = 0;
333 // If we've never heard of this property, add it.
334 if (! FindProperty (key, out first, out top)) {
335 AddProperty (Property.New (key, value));
336 return;
339 // If it has appeared once before, clobber the existing
340 // value. This emulates the previous (broken) semantics.
342 if (top - first == 1) {
343 properties [first] = Property.New (key, value);
344 return;
347 // Otherwise throw an exception (which sort of sucks,
348 // but we don't really know what to do there)
349 throw new Exception (String.Format ("Attempt to re-set multi-property '{0}' via the indexer", key));
353 public string GetFirstProperty (string key)
355 int first, top;
356 if (! FindProperty (key, out first, out top))
357 return null;
358 Property prop;
359 prop = properties [first] as Property;
360 return prop.Value;
363 public string[] GetProperties (string key)
365 int first, top;
366 if (! FindProperty (key, out first, out top))
367 return null;
369 string[] values = new string [top - first];
371 for (int i = 0; first + i < top; i++) {
372 Property prop = properties [first + i] as Property;
373 values [i] = prop.Value;
376 return values;
379 //////////////////////////
381 public override int GetHashCode ()
383 return (uri != null ? uri.ToString().GetHashCode () : 0)
384 ^ (type != null ? type.GetHashCode () : 0)
385 ^ (source != null ? source.GetHashCode () : 0);
388 //////////////////////////
390 public int CompareTo (object obj)
392 Hit otherHit = (Hit) obj;
393 // Notice that we sort by time from most to least recent
394 return DateTime.Compare (otherHit.Timestamp, this.Timestamp);