Bye-bye googledriver.
[beagle.git] / bludgeon / FileSystemObject.cs
blob0c6ae694af36017e020f966d040650079001f50a
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 public bool IsReadable {
133 get {
134 Mono.Unix.Native.Stat stat;
136 Mono.Unix.Native.Syscall.stat (Name, out stat);
138 if ((stat.st_mode & Mono.Unix.Native.FilePermissions.S_IRUSR) != 0)
139 return true;
140 else
141 return false;
145 public bool IsWritable {
146 get {
147 Mono.Unix.Native.Stat stat;
149 Mono.Unix.Native.Syscall.stat (Name, out stat);
151 if ((stat.st_mode & Mono.Unix.Native.FilePermissions.S_IWUSR) != 0)
152 return true;
153 else
154 return false;
158 virtual public string Name {
159 get {
160 if (name == null)
161 name = String.Format ("{0}.{1}{2}",
162 base_name, id, Extension != null ? Extension : "");
163 return name;
167 virtual public string ShortName {
168 get {
169 if (IsRoot)
170 return Name;
171 FileSystemObject fso = this;
172 StringBuilder sb = new StringBuilder ();
173 while (fso != null && ! fso.IsRoot) {
174 if (sb.Length > 0)
175 sb.Insert (0, "/");
176 sb.Insert (0, fso.Name);
177 fso = fso.Parent;
179 return sb.ToString ();
183 virtual public string Extension {
184 get { return null; }
187 virtual protected string GetChildUri (FileSystemObject child)
189 throw new Exception ("Invalid GetChildUri call");
192 virtual public string Uri {
193 get {
194 if (parent == null)
195 return "floating://" + Name;
196 return parent.GetChildUri (this);
200 // This should return null for objects that are not directly
201 // represented on the file system (i.e. files inside an archive)
202 virtual protected string GetChildFullName (FileSystemObject child)
204 return Path.Combine (this.FullName, child.Name);
207 virtual public string FullName {
208 get {
209 if (parent == null)
210 return Name;
211 return parent.GetChildFullName (this);
215 abstract public string MimeType { get; }
217 public DateTime Timestamp {
218 get { return timestamp; }
221 public bool HasChildren {
222 get { return children != null; }
225 public int ChildCount {
226 get { return children != null ? children.Count : 0; }
229 public ICollection Children {
230 get {
231 if (children == null)
232 throw new Exception ("Invalid request for children on " + Uri);
233 return children.Values;
237 public FileSystemObject GetChild (string name)
239 if (children == null)
240 throw new Exception ("Invalid request for child '" + name + "' on " + Uri);
241 return children [name] as FileSystemObject;
244 public virtual void AddChild (FileSystemObject child, EventTracker tracker)
246 if (children == null)
247 throw new Exception ("Can't add a child to " + Uri);
248 if (child.parent != null)
249 throw new Exception ("Can't add parented child " + child.Uri + " to " + Uri);
251 // FIXME: Need to handle the case of the added child
252 // clobbering another w/ the same name.
254 child.parent = this;
255 children [child.Name] = child;
257 if (IsRooted)
258 child.AddOnDisk (tracker);
261 public virtual void ClobberingAddChild (FileSystemObject child, FileSystemObject victim, EventTracker tracker)
263 if (children == null)
264 throw new Exception ("Can't add a child to " + Uri);
265 if (child.parent != null)
266 throw new Exception ("Can't add parented child " + child.Uri + " to " + Uri);
267 if (victim.parent != this)
268 throw new Exception ("Victim " + victim.Uri + " is not a child of " + Uri);
269 if (child.Extension != victim.Extension)
270 throw new Exception ("Extension mismatch: " + child.Extension + " vs. " + victim.Extension);
272 victim.parent = null;
273 child.parent = this;
274 child.id = victim.id;
275 child.base_name = victim.base_name;
276 child.name = null;
277 children [child.Name] = child;
279 if (IsRooted)
280 child.AddOnDisk (tracker);
283 public virtual void RemoveChild (FileSystemObject child, EventTracker tracker)
285 if (child.parent != this)
286 throw new Exception (child.Uri + " is not a child of " + Uri);
288 if (IsRooted)
289 child.DeleteOnDisk (tracker);
291 child.parent = null;
292 children.Remove (child.Name);
295 public virtual void MoveChild (FileSystemObject child, FileSystemObject new_parent, EventTracker tracker)
297 if (child.parent != this)
298 throw new Exception (child.Uri + " is not a child of " + Uri);
300 if (new_parent == null || new_parent == child.parent)
301 return;
303 // We can't move child into new_parent if child is
304 // already above new_parent in the tree.
305 if (child.IsAncestorOf (new_parent))
306 throw new Exception ("Can't move " + child.Uri + " to " + new_parent.Uri);
308 string old_full_name;
309 old_full_name = child.FullName;
311 // FIXME: We need to handle the case of the moved
312 // child clobbering another w/ the same name.
314 child.parent = new_parent;
315 this.children.Remove (child.Name);
316 new_parent.children [child.Name] = child;
318 // FIXME: What if this is not rooted, but new_parent is?
319 if (new_parent.IsRooted)
320 child.MoveOnDisk (old_full_name, tracker);
323 ////////////////////////////////////////////////////
325 protected void AllowChildren ()
327 children = new Hashtable ();
330 ////////////////////////////////////////////////////
332 // n.b. These shouldn't be public, but they have to be so that directories
333 // can manipulate their children.
335 // We assume that the FileSystemObject is in the tree when this is called.
336 virtual public void AddOnDisk (EventTracker tracker)
338 throw new Exception ("AddOnDisk undefined for " + FullName);
341 // We assume that the FileSystemObject is still in the tree (has a .parent
342 // set, etc.) when we call this.
343 virtual public void DeleteOnDisk (EventTracker tracker)
345 throw new Exception ("DeleteOnDisk undefined for " + FullName);
348 // We assume that the FileSystemObject is in the tree, it its new position
349 // when we call this.
350 virtual public void MoveOnDisk (string old_full_name, EventTracker tracker)
352 throw new Exception ("MoveOnDisk undefined for " + FullName);
355 // This checks that our on-disk state matches our tree state.
356 virtual public bool VerifyOnDisk ()
358 throw new Exception ("VerifyOnDisk undefined for " + FullName);
361 ////////////////////////////////////////////////////
363 abstract protected bool MatchesQueryPart (QueryPart part);
365 // Returns:
366 // 1 if it is a match
367 // 0 if it doesn't apply
368 // -1 if it doesn't match
369 private int MatchesMetadata (QueryPart abstract_part)
371 int is_match = 0;
373 if (abstract_part is QueryPart_Text) {
375 QueryPart_Text part;
376 part = (QueryPart_Text) abstract_part;
378 if (part.SearchTextProperties && part.Text == base_name)
379 is_match = 1;
381 } else if (abstract_part is QueryPart_Property) {
383 QueryPart_Property part;
384 part = (QueryPart_Property) abstract_part;
386 if (part.Key == "beagle:MimeType") {
387 is_match = (part.Value == this.MimeType) ? 1 : -1;
388 } else if (part.Key == "beagle:Filename") {
389 is_match = (part.Value == base_name) ? 1 : -1;
390 } else if (part.Key == "beagle:ExactFilename") {
391 is_match = (part.Value == Name) ? 1 : -1;
394 } else if (abstract_part is QueryPart_DateRange) {
396 QueryPart_DateRange part;
397 part = (QueryPart_DateRange) abstract_part;
399 is_match = (part.StartDate <= Timestamp && Timestamp <= part.EndDate) ? 1 : -1;
402 return is_match;
405 virtual public bool MatchesQuery (Query query)
407 foreach (QueryPart abstract_part in query.Parts) {
409 int is_match = 0;
411 // Note that this works because we don't
412 // allow nested or queries.
413 if (abstract_part is QueryPart_Or) {
414 QueryPart_Or part;
415 part = (QueryPart_Or) abstract_part;
417 is_match = -1;
418 foreach (QueryPart sub_part in part.SubParts) {
419 if (MatchesMetadata (sub_part) == 1
420 || MatchesQueryPart (sub_part)) {
421 is_match = 1;
422 break;
425 } else {
426 // Handle certain query parts related to file system metadata.
427 is_match = MatchesMetadata (abstract_part);
429 if (is_match == 0)
430 is_match = MatchesQueryPart (abstract_part) ? 1 : -1;
433 if (abstract_part.Logic == QueryPartLogic.Prohibited)
434 is_match = - is_match;
436 if (is_match < 0)
437 return false;
438 else if (is_match == 0)
439 throw new Exception ("This will never happen");
442 return true;
445 private void DoRecursiveQuery (Query query, ArrayList matches)
447 if (this.MatchesQuery (query))
448 matches.Add (this);
450 if (IsArchive && !SearchInArchives)
451 return;
453 if (this.HasChildren)
454 foreach (FileSystemObject child in this.Children)
455 child.DoRecursiveQuery (query, matches);
458 public ICollection RecursiveQuery (Query query)
460 ArrayList matches;
461 matches = new ArrayList ();
462 DoRecursiveQuery (query, matches);
463 return matches;