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.
29 using System
.Collections
;
30 using System
.Diagnostics
;
33 using System
.Xml
.Serialization
;
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
{
74 SpecialType special
= SpecialType
.Unknown
;
77 private FileInfo fileInfo
= null;
78 private DirectoryInfo directoryInfo
= null;
80 //////////////////////////
88 [XmlAttribute ("Uri")]
89 public string EscapedUri
{
91 return UriFu
.UriToEscapedString (uri
);
95 uri
= UriFu
.EscapedStringToUri (value);
100 public Uri ParentUri
{
101 get { return parent_uri; }
102 set { parent_uri = value; }
105 [XmlAttribute ("ParentUri")]
106 public string EscapedParentUri
{
108 if (parent_uri
== null)
111 return UriFu
.UriToEscapedString (parent_uri
);
118 parent_uri
= UriFu
.EscapedStringToUri (value);
122 // DEPRECATED: This is now stored as a property.
126 set { type = value; }
129 // DEPRECATED: This is now stored as a property.
131 public string MimeType
{
132 get { return mimeType; }
133 set { mimeType = value; }
137 public string Source
{
138 get { return source; }
139 set { source = value; }
143 public object SourceObject
{
144 get { return sourceObject; }
145 set { sourceObject = value; }
149 public double Score
{
150 get { return score; }
151 set { score = value; }
154 //////////////////////////
156 private void SpecialHandling ()
158 if (special
!= SpecialType
.Unknown
)
162 path
= uri
.LocalPath
;
163 if (File
.Exists (path
))
164 special
= SpecialType
.File
;
165 else if (Directory
.Exists (path
))
166 special
= SpecialType
.Directory
;
168 special
= SpecialType
.Invalid
;
171 if (special
== SpecialType
.Unknown
)
172 special
= SpecialType
.None
;
175 public bool IsValid
{
176 get { SpecialHandling (); return special != SpecialType.Invalid; }
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; }
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; }
208 public FileSystemInfo FileSystemInfo
{
211 return (FileSystemInfo
) FileInfo
;
212 else if (IsDirectory
)
213 return (FileSystemInfo
) DirectoryInfo
;
220 public FileInfo FileInfo
{
222 if (fileInfo
== null && IsFile
)
223 fileInfo
= new FileInfo (Path
);
229 public DirectoryInfo DirectoryInfo
{
231 if (directoryInfo
== null && IsDirectory
)
232 directoryInfo
= new DirectoryInfo (Path
);
233 return directoryInfo
;
237 //////////////////////////
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.
254 properties
.Insert (loc
, prop
);
257 public void AddProperty (ICollection props
)
259 properties
.AddRange (props
);
264 private bool FindProperty (string key
, out int first
, out int top
)
269 int range
= properties
.Count
- 1;
272 // O(log n + |range|)-time algorithm for 1-d range query
275 int mid
= first
+ (range
/2);
277 prop
= properties
[mid
] as Property
;
278 // Properties are sorted first by Key, then by Value
279 // We only need to compare the key of the middle element to the key in parameter
280 if (String
.Compare (key
, prop
.Key
) > 0) {
281 // minimum item in right subtree is smaller
291 // - either the previous item in the BST
292 // - or the next to the previous item in the BST
293 prop
= (Property
) properties
[first
];
296 if (first
>= properties
.Count
)
298 prop
= (Property
) properties
[first
];
303 // Since range will be small, do a linear scan from here.
304 // We could do another BST traversal at the cost of O(log-n),
305 // but its not worth it.
306 while (top
< properties
.Count
) {
307 prop
= properties
[top
] as Property
;
316 public string this [string key
] {
319 if (! FindProperty (key
, out first
, out top
))
322 if (top
- first
!= 1) {
323 Logger
.Log
.Warn ("Accessed multi-property key with Hit's indexer.");
328 prop
= properties
[first
] as Property
;
333 int first
= 0, top
= 0;
335 // If we've never heard of this property, add it.
336 if (! FindProperty (key
, out first
, out top
)) {
337 AddProperty (Property
.New (key
, value));
341 // If it has appeared once before, clobber the existing
342 // value. This emulates the previous (broken) semantics.
344 if (top
- first
== 1) {
345 properties
[first
] = Property
.New (key
, value);
349 // Otherwise throw an exception (which sort of sucks,
350 // but we don't really know what to do there)
351 throw new Exception (String
.Format ("Attempt to re-set multi-property '{0}' via the indexer", key
));
355 public string GetFirstProperty (string key
)
358 if (! FindProperty (key
, out first
, out top
))
361 prop
= properties
[first
] as Property
;
365 public string[] GetProperties (string key
)
368 if (! FindProperty (key
, out first
, out top
))
371 string[] values
= new string [top
- first
];
373 for (int i
= 0; first
+ i
< top
; i
++) {
374 Property prop
= properties
[first
+ i
] as Property
;
375 values
[i
] = prop
.Value
;
381 //////////////////////////
383 public override int GetHashCode ()
385 return (uri
!= null ? uri
.ToString().GetHashCode () : 0)
386 ^
(type
!= null ? type
.GetHashCode () : 0)
387 ^
(source
!= null ? source
.GetHashCode () : 0);
390 //////////////////////////
392 public int CompareTo (object obj
)
394 Hit otherHit
= (Hit
) obj
;
395 // Notice that we sort by time from most to least recent
396 return DateTime
.Compare (otherHit
.Timestamp
, this.Timestamp
);