2006-02-27 Gabor Kelemen <kelemeng@gnome.hu>
[beagle.git] / bludgeon / FileSystemObject.cs
blob078d9dbaea51d841c18e1f48ff70dab65834d03d
2 using System;
3 using System.Collections;
4 using System.IO;
5 using System.Text;
7 using Beagle.Util;
8 using Beagle;
11 Policy:
12 Changes to the file system are made when the object is added to
13 a rooted tree. Roots are automatically instantiated when they
14 are created, and are never associated with any events.
17 namespace Bludgeon {
19 abstract public class FileSystemObject {
21 // TURN THIS ON TO TEST THE ARCHIVE PATCH!
22 static public bool SearchInArchives = false;
24 private static int next_id = 0;
26 private FileSystemObject parent;
27 private string base_name;
28 private int id;
29 private string name;
30 protected Hashtable children;
31 protected DateTime timestamp;
33 ////////////////////////////////////////////////////
35 static DateTime base_time;
36 const int seconds_per_year = 60 * 60 * 24 * 365;
38 static FileSystemObject ()
40 base_time = DateTime.Now;
43 static public DateTime PickTimestamp ()
45 Random random = new Random ();
46 return base_time.AddSeconds (- random.Next (seconds_per_year));
49 static public void PickTimestampRange (out DateTime a, out DateTime b)
51 a = PickTimestamp ();
52 b = PickTimestamp ();
53 if (b < a) {
54 DateTime tmp = a;
55 a = b;
56 b = tmp;
60 protected FileSystemObject ()
62 PickName ();
65 protected void PickName ()
67 id = next_id;
68 ++next_id;
70 base_name = Token.GetRandomWithUnicode ();
72 name = null;
75 protected void ClearName ()
77 base_name = null;
80 ////////////////////////////////////////////////////
82 public FileSystemObject Parent {
83 get { return parent; }
86 virtual public bool IsRoot {
87 get { return false; }
90 // Returns true if this object is part of a tree with
91 // a root at the top.
92 public bool IsRooted {
93 get {
94 FileSystemObject curr;
95 curr = this;
96 while (curr != null) {
97 if (curr.IsRoot)
98 return true;
99 curr = curr.Parent;
101 return false;
105 // By definition, an object is an ancestor of itself.
106 public bool IsAncestorOf (FileSystemObject fso)
108 if (! this.HasChildren)
109 return false;
110 while (fso != null) {
111 if (this == fso)
112 return true;
113 fso = fso.Parent;
115 return false;
118 // Is this an archive?
119 public bool IsArchive {
120 get {
121 return HasChildren && (this is FileObject);
125 // Is this FileSystemObject actually a child of an archive?
126 public bool IsInArchive {
127 get {
128 return Parent != null && (Parent is FileObject || Parent.IsInArchive);
132 virtual public string Name {
133 get {
134 if (name == null)
135 name = String.Format ("{0}.{1}{2}",
136 base_name, id, Extension != null ? Extension : "");
137 return name;
141 virtual public string ShortName {
142 get {
143 if (IsRoot)
144 return Name;
145 FileSystemObject fso = this;
146 StringBuilder sb = new StringBuilder ();
147 while (fso != null && ! fso.IsRoot) {
148 if (sb.Length > 0)
149 sb.Insert (0, "/");
150 sb.Insert (0, fso.Name);
151 fso = fso.Parent;
153 return sb.ToString ();
157 virtual public string Extension {
158 get { return null; }
161 virtual protected string GetChildUri (FileSystemObject child)
163 throw new Exception ("Invalid GetChildUri call");
166 virtual public string Uri {
167 get {
168 if (parent == null)
169 return "floating://" + Name;
170 return parent.GetChildUri (this);
174 // This should return null for objects that are not directly
175 // represented on the file system (i.e. files inside an archive)
176 virtual protected string GetChildFullName (FileSystemObject child)
178 return Path.Combine (this.FullName, child.Name);
181 virtual public string FullName {
182 get {
183 if (parent == null)
184 return Name;
185 return parent.GetChildFullName (this);
189 abstract public string MimeType { get; }
191 public DateTime Timestamp {
192 get { return timestamp; }
195 public bool HasChildren {
196 get { return children != null; }
199 public int ChildCount {
200 get { return children != null ? children.Count : 0; }
203 public ICollection Children {
204 get {
205 if (children == null)
206 throw new Exception ("Invalid request for children on " + Uri);
207 return children.Values;
211 public FileSystemObject GetChild (string name)
213 if (children == null)
214 throw new Exception ("Invalid request for child '" + name + "' on " + Uri);
215 return children [name] as FileSystemObject;
218 public virtual void AddChild (FileSystemObject child, EventTracker tracker)
220 if (children == null)
221 throw new Exception ("Can't add a child to " + Uri);
222 if (child.parent != null)
223 throw new Exception ("Can't add parented child " + child.Uri + " to " + Uri);
225 // FIXME: Need to handle the case of the added child
226 // clobbering another w/ the same name.
228 child.parent = this;
229 children [child.Name] = child;
231 if (IsRooted)
232 child.AddOnDisk (tracker);
235 public virtual void ClobberingAddChild (FileSystemObject child, FileSystemObject victim, EventTracker tracker)
237 if (children == null)
238 throw new Exception ("Can't add a child to " + Uri);
239 if (child.parent != null)
240 throw new Exception ("Can't add parented child " + child.Uri + " to " + Uri);
241 if (victim.parent != this)
242 throw new Exception ("Victim " + victim.Uri + " is not a child of " + Uri);
243 if (child.Extension != victim.Extension)
244 throw new Exception ("Extension mismatch: " + child.Extension + " vs. " + victim.Extension);
246 victim.parent = null;
247 child.parent = this;
248 child.id = victim.id;
249 child.base_name = victim.base_name;
250 child.name = null;
251 children [child.Name] = child;
253 if (IsRooted)
254 child.AddOnDisk (tracker);
257 public virtual void RemoveChild (FileSystemObject child, EventTracker tracker)
259 if (child.parent != this)
260 throw new Exception (child.Uri + " is not a child of " + Uri);
262 if (IsRooted)
263 child.DeleteOnDisk (tracker);
265 child.parent = null;
266 children.Remove (child.Name);
269 public virtual void MoveChild (FileSystemObject child, FileSystemObject new_parent, EventTracker tracker)
271 if (child.parent != this)
272 throw new Exception (child.Uri + " is not a child of " + Uri);
274 if (new_parent == null || new_parent == child.parent)
275 return;
277 // We can't move child into new_parent if child is
278 // already above new_parent in the tree.
279 if (child.IsAncestorOf (new_parent))
280 throw new Exception ("Can't move " + child.Uri + " to " + new_parent.Uri);
282 string old_full_name;
283 old_full_name = child.FullName;
285 // FIXME: We need to handle the case of the moved
286 // child clobbering another w/ the same name.
288 child.parent = new_parent;
289 this.children.Remove (child.Name);
290 new_parent.children [child.Name] = child;
292 // FIXME: What if this is not rooted, but new_parent is?
293 if (new_parent.IsRooted)
294 child.MoveOnDisk (old_full_name, tracker);
297 ////////////////////////////////////////////////////
299 protected void AllowChildren ()
301 children = new Hashtable ();
304 ////////////////////////////////////////////////////
306 // n.b. These shouldn't be public, but they have to be so that directories
307 // can manipulate their children.
309 // We assume that the FileSystemObject is in the tree when this is called.
310 virtual public void AddOnDisk (EventTracker tracker)
312 throw new Exception ("AddOnDisk undefined for " + FullName);
315 // We assume that the FileSystemObject is still in the tree (has a .parent
316 // set, etc.) when we call this.
317 virtual public void DeleteOnDisk (EventTracker tracker)
319 throw new Exception ("DeleteOnDisk undefined for " + FullName);
322 // We assume that the FileSystemObject is in the tree, it its new position
323 // when we call this.
324 virtual public void MoveOnDisk (string old_full_name, EventTracker tracker)
326 throw new Exception ("MoveOnDisk undefined for " + FullName);
329 // This checks that our on-disk state matches our tree state.
330 virtual public bool VerifyOnDisk ()
332 throw new Exception ("VerifyOnDisk undefined for " + FullName);
335 ////////////////////////////////////////////////////
337 abstract protected bool MatchesQueryPart (QueryPart part);
339 private int MatchesMetadata (QueryPart abstract_part)
341 int is_match = 0;
343 if (abstract_part is QueryPart_Text) {
345 QueryPart_Text part;
346 part = (QueryPart_Text) abstract_part;
348 if (part.SearchTextProperties && part.Text == base_name)
349 is_match = 1;
351 } else if (abstract_part is QueryPart_Property) {
353 QueryPart_Property part;
354 part = (QueryPart_Property) abstract_part;
356 if (part.Key == "beagle:MimeType") {
357 is_match = (part.Value == this.MimeType) ? 1 : -1;
358 } else if (part.Key == "beagle:Filename") {
359 is_match = (part.Value == base_name) ? 1 : -1;
360 } else if (part.Key == "beagle:ExactFilename") {
361 is_match = (part.Value == Name) ? 1 : -1;
364 } else if (abstract_part is QueryPart_DateRange) {
366 QueryPart_DateRange part;
367 part = (QueryPart_DateRange) abstract_part;
369 is_match = (part.StartDate <= Timestamp && Timestamp <= part.EndDate) ? 1 : -1;
372 return is_match;
375 virtual public bool MatchesQuery (Query query)
377 foreach (QueryPart abstract_part in query.Parts) {
379 int is_match = 0;
381 // Note that this works because we don't
382 // allow nested or queries.
383 if (abstract_part is QueryPart_Or) {
384 QueryPart_Or part;
385 part = (QueryPart_Or) abstract_part;
387 is_match = -1;
388 foreach (QueryPart sub_part in part.SubParts) {
389 if (MatchesMetadata (sub_part) == 1
390 || MatchesQueryPart (sub_part)) {
391 is_match = 1;
392 break;
395 } else {
396 // Handle certain query parts related to file system metadata.
397 is_match = MatchesMetadata (abstract_part);
399 if (is_match == 0)
400 is_match = MatchesQueryPart (abstract_part) ? 1 : -1;
403 if (abstract_part.Logic == QueryPartLogic.Prohibited)
404 is_match = - is_match;
406 if (is_match < 0)
407 return false;
408 else if (is_match == 0)
409 throw new Exception ("This will never happen");
412 return true;
415 private void DoRecursiveQuery (Query query, ArrayList matches)
417 if (this.MatchesQuery (query))
418 matches.Add (this);
420 if (IsArchive && !SearchInArchives)
421 return;
423 if (this.HasChildren)
424 foreach (FileSystemObject child in this.Children)
425 child.DoRecursiveQuery (query, matches);
428 public ICollection RecursiveQuery (Query query)
430 ArrayList matches;
431 matches = new ArrayList ();
432 DoRecursiveQuery (query, matches);
433 return matches;