3 using System
.Collections
;
11 public class FileModel
{
13 static Random random
= new Random ();
14 static private ArrayList roots
= new ArrayList ();
16 static public ICollection Roots { get { return roots; }
}
18 static public void AddRoot (FileModel root
)
21 throw new Exception ("Attempted to add non-root as root");
26 static public FileModel
PickRoot ()
30 else if (roots
.Count
== 1)
31 return roots
[0] as FileModel
;
33 return roots
[random
.Next (roots
.Count
)] as FileModel
;
37 // Note that the probability of a FileModel being picked
38 // by any of these "picker" methods is not uniform.
39 // The two-step process where we first pick a root messes
44 // Can be either a file or directory
45 static public FileModel
PickNonRoot ()
49 return root
.PickDescendant ();
52 static public FileModel
PickFile ()
56 return root
.PickFileDescendant ();
59 static public FileModel
PickAnyDirectory ()
63 return root
.PickDirectory ();
66 static public FileModel
PickNonRootDirectory ()
70 return root
.PickDirectoryDescendant ();
73 //////////////////////////////////////////////////////////////
75 // Properties of the root directory:
76 // name contains full path of BEAGLE_HOME
79 // children is non-null
81 // Properties of a non-root directory:
85 // children is non-null
87 // Properties of a file:
89 // parent is non-null, and is a directory
93 private string name
= null;
94 private DateTime mtime
;
95 private FileModel parent
= null;
96 private string [] body
= null;
97 private Hashtable children
= null;
99 //////////////////////////////////////////////////////////////
102 get { return parent == null; }
105 public bool IsDirectory
{
106 get { return body == null; }
110 get { return body != null; }
114 get { return IsDirectory ? "directory" : "file"; }
121 public DateTime Mtime
{
122 get { return mtime; }
125 public string FullName
{
126 get { return IsRoot ? Name : Path.Combine (parent.FullName, name); }
129 public string ShortName
{
130 get { return IsRoot ? Path.GetFileName (Name) : Path.Combine (parent.ShortName, name); }
134 get { return UriFu.PathToFileUri (FullName); }
137 public string [] Body
{
141 public FileModel Parent
{
142 get { return parent; }
145 public ICollection Children
{
146 get { return children.Values; }
155 sum
= 1; // count ourselves
156 foreach (FileModel child
in children
.Values
)
162 // IsAbove is easier to think about than IsAncestorOf
163 public bool IsAbove (FileModel other
)
165 return this == other
.parent
166 || (other
.parent
!= null && IsAbove (other
.parent
));
169 // IsBelow is easier to thik about than IsDescendantOf
170 public bool IsBelow (FileModel other
)
172 return other
.IsAbove (this);
175 //////////////////////////////////////////////////////////////
177 private void RecursiveListAdd (ArrayList list
,
182 if (add_self
&& ((add_dirs
&& IsDirectory
) || (add_files
&& IsFile
)))
185 if (children
!= null)
186 foreach (FileModel file
in children
.Values
)
187 file
.RecursiveListAdd (list
, true, add_dirs
, add_files
);
190 public ArrayList
GetDescendants ()
192 ArrayList list
= new ArrayList ();
193 RecursiveListAdd (list
, false, true, true);
197 public ArrayList
GetFileDescendants ()
199 ArrayList list
= new ArrayList ();
200 RecursiveListAdd (list
, false, false, true);
205 public ArrayList
GetDirectories ()
207 ArrayList list
= new ArrayList ();
208 RecursiveListAdd (list
, true, true, false);
212 // Never includes ourself
213 public ArrayList
GetDirectoryDescendants ()
215 ArrayList list
= new ArrayList ();
216 RecursiveListAdd (list
, false, true, false);
220 //////////////////////////////////////////////////////////////
222 public FileModel
PickDescendant ()
225 all
= GetDescendants ();
228 return all
[random
.Next (all
.Count
)] as FileModel
;
231 public FileModel
PickFileDescendant ()
234 all
= GetFileDescendants ();
237 return all
[random
.Next (all
.Count
)] as FileModel
;
240 public FileModel
PickDirectoryDescendant ()
243 all
= GetDirectoryDescendants ();
246 return all
[random
.Next (all
.Count
)] as FileModel
;
249 // This can return the root
250 public FileModel
PickDirectory ()
253 all
= GetDirectories ();
254 return all
[random
.Next (all
.Count
)] as FileModel
;
257 //////////////////////////////////////////////////////////////
259 public bool BodyContains (string token
)
264 // FIXME: Do a binary search (or something smarter)
266 for (int i
= 0; i
< body
.Length
; ++i
)
267 if (body
[i
] == token
)
272 public bool Contains (string token
)
274 return name
== token
|| BodyContains (token
);
277 //////////////////////////////////////////////////////////////
279 static DateTime base_time
;
280 const int seconds_per_year
= 60 * 60 * 24 * 365;
282 // Picks a random time in the last year
283 static public DateTime
PickDateTime ()
285 return base_time
.AddSeconds (- random
.Next (seconds_per_year
));
288 //////////////////////////////////////////////////////////////
292 base_time
= DateTime
.Now
;
297 // Pick a random timestamp for every file.
298 mtime
= PickDateTime ();
301 public static FileModel
NewRoot ()
303 FileModel root
= new FileModel ();
304 root
.name
= PathFinder
.HomeDir
;
305 root
.children
= new Hashtable ();
306 root
.mtime
= Directory
.GetLastWriteTimeUtc (root
.FullName
);
310 // Creates a randomly-named new directory.
311 // Avoid name collisions with existing files.
312 public FileModel
NewDirectory ()
315 throw new ArgumentException ("parent must be a directory");
317 // no more names left
318 if (children
.Count
== Token
.Count
)
322 child
= new FileModel ();
323 child
.name
= PickName (this);
324 child
.children
= new Hashtable ();
327 children
[child
.name
] = child
;
329 // Actually create the directory
330 Directory
.CreateDirectory (child
.FullName
);
331 child
.mtime
= Directory
.GetLastWriteTimeUtc (child
.FullName
);
336 public FileModel
NewFile ()
339 throw new ArgumentException ("parent must be a directory");
341 // no more names left
342 if (children
.Count
== Token
.Count
)
346 child
= new FileModel ();
347 child
.name
= PickName (this);
348 child
.body
= NewBody (10);
351 children
[child
.name
] = child
;
359 //////////////////////////////////////////////////////////////
363 public void Grow (int depth
)
365 const int num_dirs
= 2;
366 const int num_files
= 5;
369 for (int i
= 0; i
< num_dirs
; ++i
) {
371 file
= NewDirectory ();
373 file
.Grow (depth
- 1);
377 for (int i
= 0; i
< num_files
; ++i
)
381 //////////////////////////////////////////////////////////////
383 // Basic file system operations
393 public void Delete ()
396 throw new Exception ("Can't delete the root!");
399 Directory
.Delete (FullName
, true); // recursive
401 File
.Delete (FullName
);
403 parent
.children
.Remove (name
);
407 // If the move would cause a filename collision or is
408 // otherwise impossible, return false. Return true if the
409 // move actually happens.
410 public bool MoveTo (FileModel new_parent
, // or null, to just rename
411 string new_name
) // or null, to just move
413 if (! new_parent
.IsDirectory
)
414 throw new ArgumentException ("Parent must be a directory");
417 throw new ArgumentException ("Can't move a root");
420 if (this == new_parent
|| this.IsAbove (new_parent
))
424 old_path
= this.FullName
;
426 if (new_parent
== null)
427 new_parent
= this.parent
;
428 if (new_name
== null)
429 new_name
= this.name
;
431 // check for a filename collision
432 if (new_parent
.children
.Contains (new_name
))
435 // modify the data structure
436 this.parent
.children
.Remove (this.name
);
437 this.parent
= new_parent
;
438 this.name
= new_name
;
439 this.parent
.children
[this.name
] = this;
442 new_path
= Path
.Combine (new_parent
.FullName
, new_name
);
444 if (this.IsDirectory
)
445 Directory
.Move (old_path
, new_path
);
447 File
.Move (old_path
, new_path
);
453 //////////////////////////////////////////////////////////////
455 // Useful utility functions
457 static private string PickName (FileModel p
)
461 pick
= Token
.GetRandom ();
462 } while (p
.children
.Contains (pick
));
466 static private string [] NewBody (int size
)
469 body
= new string [size
];
470 for (int i
= 0; i
< size
; ++i
)
471 body
[i
] = Token
.GetRandom ();
476 private void Write ()
479 writer
= new StreamWriter (FullName
);
480 for (int i
= 0; i
< body
.Length
; ++i
)
481 writer
.WriteLine (body
[i
]);
485 info
= new FileInfo (FullName
);
486 info
.LastWriteTime
= Mtime
;
489 //////////////////////////////////////////////////////////////
492 // Code to determine a a file will match a particular query
495 private bool MatchesQueryPart (QueryPart abstract_part
)
500 if (abstract_part
is QueryPart_Text
) {
502 part
= abstract_part
as QueryPart_Text
;
504 if ((part
.SearchTextProperties
&& Name
== part
.Text
)
505 || (part
.SearchFullText
&& BodyContains (part
.Text
)))
508 } else if (abstract_part
is QueryPart_Or
) {
510 part
= abstract_part
as QueryPart_Or
;
512 foreach (QueryPart sub_part
in part
.SubParts
) {
513 if (MatchesQueryPart (sub_part
)) {
518 } else if (abstract_part
is QueryPart_Property
) {
519 QueryPart_Property part
;
520 part
= abstract_part
as QueryPart_Property
;
522 if (part
.Key
== "beagle:MimeType") {
523 if (part
.Value
== "inode/directory")
524 is_match
= IsDirectory
;
525 else if (part
.Value
== "text/plain")
529 } else if (part
.Key
== "beagle:ExactFilename") {
530 is_match
= (Name
== part
.Value
);
532 throw new Exception ("Unsupported property " + part
.Key
);
534 } else if (abstract_part
is QueryPart_DateRange
) {
535 QueryPart_DateRange part
;
536 part
= abstract_part
as QueryPart_DateRange
;
538 // FIXME: We assume that the query refers to the file timestamp.
539 // Instead, we should explicitly check part.Key.
540 is_match
= (part
.StartDate
<= Mtime
&& Mtime
<= part
.EndDate
);
543 throw new Exception ("Unsupported part");
546 if (abstract_part
.Logic
== QueryPartLogic
.Prohibited
)
547 is_match
= ! is_match
;
552 public bool MatchesQuery (Query query
)
554 // We assume the root node never matches any query.
558 foreach (QueryPart part
in query
.Parts
) {
559 if (! MatchesQueryPart (part
))
566 private void RecursiveQueryCheck (ArrayList match_list
, Query query
)
568 if (MatchesQuery (query
))
569 match_list
.Add (this);
571 if (children
!= null)
572 foreach (FileModel file
in children
.Values
)
573 file
.RecursiveQueryCheck (match_list
, query
);
576 public ArrayList
GetMatchingDescendants (Query query
)
578 ArrayList match_list
;
579 match_list
= new ArrayList ();
580 RecursiveQueryCheck (match_list
, query
);