3 using System
.Collections
;
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.
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
;
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
)
60 protected FileSystemObject ()
65 protected void PickName ()
70 base_name
= Token
.GetRandomWithUnicode ();
75 protected void ClearName ()
80 ////////////////////////////////////////////////////
82 public FileSystemObject Parent
{
83 get { return parent; }
86 virtual public bool IsRoot
{
90 // Returns true if this object is part of a tree with
92 public bool IsRooted
{
94 FileSystemObject curr
;
96 while (curr
!= null) {
105 // By definition, an object is an ancestor of itself.
106 public bool IsAncestorOf (FileSystemObject fso
)
108 if (! this.HasChildren
)
110 while (fso
!= null) {
118 // Is this an archive?
119 public bool IsArchive
{
121 return HasChildren
&& (this is FileObject
);
125 // Is this FileSystemObject actually a child of an archive?
126 public bool IsInArchive
{
128 return Parent
!= null && (Parent
is FileObject
|| Parent
.IsInArchive
);
132 virtual public string Name
{
135 name
= String
.Format ("{0}.{1}{2}",
136 base_name
, id
, Extension
!= null ? Extension
: "");
141 virtual public string ShortName
{
145 FileSystemObject fso
= this;
146 StringBuilder sb
= new StringBuilder ();
147 while (fso
!= null && ! fso
.IsRoot
) {
150 sb
.Insert (0, fso
.Name
);
153 return sb
.ToString ();
157 virtual public string Extension
{
161 virtual protected string GetChildUri (FileSystemObject child
)
163 throw new Exception ("Invalid GetChildUri call");
166 virtual public string Uri
{
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
{
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
{
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.
229 children
[child
.Name
] = child
;
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;
248 child
.id
= victim
.id
;
249 child
.base_name
= victim
.base_name
;
251 children
[child
.Name
] = child
;
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
);
263 child
.DeleteOnDisk (tracker
);
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
)
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
)
343 if (abstract_part
is QueryPart_Text
) {
346 part
= (QueryPart_Text
) abstract_part
;
348 if (part
.SearchTextProperties
&& part
.Text
== base_name
)
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;
375 virtual public bool MatchesQuery (Query query
)
377 foreach (QueryPart abstract_part
in query
.Parts
) {
381 // Note that this works because we don't
382 // allow nested or queries.
383 if (abstract_part
is QueryPart_Or
) {
385 part
= (QueryPart_Or
) abstract_part
;
388 foreach (QueryPart sub_part
in part
.SubParts
) {
389 if (MatchesMetadata (sub_part
) == 1
390 || MatchesQueryPart (sub_part
)) {
396 // Handle certain query parts related to file system metadata.
397 is_match
= MatchesMetadata (abstract_part
);
400 is_match
= MatchesQueryPart (abstract_part
) ? 1 : -1;
403 if (abstract_part
.Logic
== QueryPartLogic
.Prohibited
)
404 is_match
= - is_match
;
408 else if (is_match
== 0)
409 throw new Exception ("This will never happen");
415 private void DoRecursiveQuery (Query query
, ArrayList matches
)
417 if (this.MatchesQuery (query
))
420 if (IsArchive
&& !SearchInArchives
)
423 if (this.HasChildren
)
424 foreach (FileSystemObject child
in this.Children
)
425 child
.DoRecursiveQuery (query
, matches
);
428 public ICollection
RecursiveQuery (Query query
)
431 matches
= new ArrayList ();
432 DoRecursiveQuery (query
, matches
);