Clean up SparkleLib API

This commit is contained in:
Hylke Bons 2012-02-08 20:42:29 +01:00
parent 7ad2dbf99a
commit 3370a283ea
2 changed files with 266 additions and 280 deletions

View file

@ -61,7 +61,7 @@ namespace SparkleLib {
public override List<string> ExcludePaths {
get {
List<string> rules = new List<string> ();
rules.Add (Path.DirectorySeparatorChar + ".git");
rules.Add (".git");
return rules;
}
@ -96,12 +96,12 @@ namespace SparkleLib {
}
private void CalculateSizes ()
private void UpdateSizes ()
{
double size = CalculateSize (
double size = CalculateSizes (
new DirectoryInfo (LocalPath));
double history_size = CalculateSize (
double history_size = CalculateSizes (
new DirectoryInfo (Path.Combine (LocalPath, ".git")));
string size_file_path = new string [] {LocalPath, ".git", "repo_size"}.Combine ();
@ -194,7 +194,7 @@ namespace SparkleLib {
public override bool SyncUp ()
{
if (AnyDifferences) {
if (HasLocalChanges) {
Add ();
string message = FormatCommitMessage ();
@ -250,7 +250,7 @@ namespace SparkleLib {
git.WaitForExit ();
CalculateSizes ();
UpdateSizes ();
if (git.ExitCode == 0)
return true;
@ -307,7 +307,7 @@ namespace SparkleLib {
git.WaitForExit ();
CalculateSizes ();
UpdateSizes ();
if (git.ExitCode == 0) {
Rebase ();
@ -319,7 +319,7 @@ namespace SparkleLib {
}
public override bool AnyDifferences {
public override bool HasLocalChanges {
get {
PrepareDirectories (LocalPath);
@ -414,7 +414,7 @@ namespace SparkleLib {
{
DisableWatching ();
if (AnyDifferences) {
if (HasLocalChanges) {
Add ();
string commit_message = FormatCommitMessage ();
@ -429,7 +429,7 @@ namespace SparkleLib {
if (git.ExitCode != 0) {
SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Conflict detected. Trying to get out...");
while (AnyDifferences)
while (HasLocalChanges)
ResolveConflict ();
SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Conflict resolved.");
@ -809,34 +809,15 @@ namespace SparkleLib {
}
public override bool UsesNotificationCenter
{
get {
string file_path = SparkleHelpers.CombineMore (LocalPath, ".git", "disable_notification_center");
return !File.Exists (file_path);
}
}
public override void CreateInitialChangeSet ()
{
base.CreateInitialChangeSet ();
SyncUp ();
}
// Recursively gets a folder's size in bytes
public override double CalculateSize (DirectoryInfo parent)
private double CalculateSizes (DirectoryInfo parent)
{
if (!Directory.Exists (parent.ToString ()))
return 0;
double size = 0;
// Ignore the temporary 'rebase-apply' and '.tmp' directories. This prevents potential
// crashes when files are being queried whilst the files have already been deleted.
if (parent.Name.Equals ("rebase-apply") ||
parent.Name.Equals (".tmp"))
if (parent.Name.Equals ("rebase-apply"))
return 0;
try {
@ -848,7 +829,7 @@ namespace SparkleLib {
}
foreach (DirectoryInfo directory in parent.GetDirectories ())
size += CalculateSize (directory);
size += CalculateSizes (directory);
} catch (Exception) {
return 0;

View file

@ -60,12 +60,11 @@ namespace SparkleLib {
public readonly string Name;
public readonly Uri Url;
public abstract bool AnyDifferences { get; }
public abstract bool HasLocalChanges { get; }
public abstract string Identifier { get; }
public abstract string CurrentRevision { get; }
public abstract bool SyncUp ();
public abstract bool SyncDown ();
public abstract double CalculateSize (DirectoryInfo parent);
public abstract bool HasUnsyncedChanges { get; set; }
public abstract bool HasRemoteChanges { get; }
public abstract List<string> ExcludePaths { get; }
@ -92,6 +91,57 @@ namespace SparkleLib {
public event ChangesDetectedEventHandler ChangesDetected;
public bool ServerOnline {
get {
return this.server_online;
}
}
public SyncStatus Status {
get {
return this.status;
}
}
public double ProgressPercentage {
get {
return this.progress_percentage;
}
}
public string ProgressSpeed {
get {
return this.progress_speed;
}
}
public virtual string [] UnsyncedFilePaths {
get {
return new string [0];
}
}
public bool IsSyncing {
get {
return (Status == SyncStatus.SyncUp ||
Status == SyncStatus.SyncDown ||
this.is_buffering);
}
}
public bool IsBuffering {
get {
return this.is_buffering;
}
}
public SparkleRepoBase (string path)
{
LocalPath = path;
@ -139,7 +189,7 @@ namespace SparkleLib {
{
// Sync up everything that changed
// since we've been offline
if (AnyDifferences) {
if (HasLocalChanges) {
DisableWatching ();
SyncUpBase ();
@ -154,58 +204,9 @@ namespace SparkleLib {
}
public bool ServerOnline {
get {
return this.server_online;
}
}
public SyncStatus Status {
get {
return this.status;
}
}
public double ProgressPercentage {
get {
return this.progress_percentage;
}
}
public string ProgressSpeed {
get {
return this.progress_speed;
}
}
public virtual string [] UnsyncedFilePaths {
get {
return new string [0];
}
}
public string Domain {
get {
// TODO: use Uri class
Regex regex = new Regex (@"(@|://)([a-z0-9\.-]+)(/|:)");
Match match = regex.Match (SparkleConfig.DefaultConfig.GetUrlForFolder (Name));
if (match.Success)
return match.Groups [2].Value;
else
return null;
}
}
protected void OnConflictResolved ()
{
HasUnsyncedChanges = true;
HasUnsyncedChanges = true; // ?
if (ConflictResolved != null)
ConflictResolved ();
@ -217,28 +218,6 @@ namespace SparkleLib {
}
public virtual bool UsesNotificationCenter {
get {
return true;
}
}
public string RemoteName {
get {
string url = SparkleConfig.DefaultConfig.GetUrlForFolder (Name);
return Path.GetFileNameWithoutExtension (url);
}
}
public bool IsBuffering {
get {
return this.is_buffering;
}
}
// Disposes all resourses of this object
public void Dispose ()
{
@ -248,114 +227,6 @@ namespace SparkleLib {
}
private void CreateWatcher ()
{
this.watcher = new SparkleWatcher (LocalPath);
this.watcher.ChangeEvent += delegate (FileSystemEventArgs args) {
OnFileActivity (args);
};
}
public void CreateListener ()
{
this.listener = SparkleListenerFactory.CreateListener (Name, Identifier);
if (this.listener.IsConnected) {
this.poll_interval = this.long_interval;
new Thread (new ThreadStart (delegate {
if (!IsSyncing && HasRemoteChanges)
SyncDownBase ();
})).Start ();
}
// Stop polling when the connection to the irc channel is succesful
this.listener.Connected += delegate {
this.poll_interval = this.long_interval;
this.last_poll = DateTime.Now;
if (!IsSyncing) {
// Check for changes manually one more time
if (HasRemoteChanges)
SyncDownBase ();
// Push changes that were made since the last disconnect
if (HasUnsyncedChanges)
SyncUpBase ();
}
};
// Start polling when the connection to the irc channel is lost
this.listener.Disconnected += delegate {
this.poll_interval = this.short_interval;
SparkleHelpers.DebugInfo (Name, "Falling back to polling");
};
// Fetch changes when there is a message in the irc channel
this.listener.Received += delegate (SparkleAnnouncement announcement) {
string identifier = Identifier;
if (announcement.FolderIdentifier.Equals (identifier) &&
!announcement.Message.Equals (CurrentRevision)) {
while (this.IsSyncing)
System.Threading.Thread.Sleep (100);
SparkleHelpers.DebugInfo ("Listener", "Syncing due to announcement");
SyncDownBase ();
} else {
if (announcement.FolderIdentifier.Equals (identifier))
SparkleHelpers.DebugInfo ("Listener", "Not syncing, message is for current revision");
}
};
// Start listening
if (!this.listener.IsConnected && !this.listener.IsConnecting)
this.listener.Connect ();
}
public bool IsSyncing {
get {
return (Status == SyncStatus.SyncUp ||
Status == SyncStatus.SyncDown ||
this.is_buffering);
}
}
private void CheckForChanges ()
{
lock (this.change_lock) {
if (this.has_changed) {
if (this.size_buffer.Count >= 4)
this.size_buffer.RemoveAt (0);
DirectoryInfo dir_info = new DirectoryInfo (LocalPath);
this.size_buffer.Add (CalculateSize (dir_info));
if (this.size_buffer.Count >= 4 &&
this.size_buffer [0].Equals (this.size_buffer [1]) &&
this.size_buffer [1].Equals (this.size_buffer [2]) &&
this.size_buffer [2].Equals (this.size_buffer [3])) {
SparkleHelpers.DebugInfo ("Local", "[" + Name + "] Changes have settled.");
this.is_buffering = false;
this.has_changed = false;
DisableWatching ();
while (AnyDifferences)
SyncUpBase ();
EnableWatching ();
}
}
}
}
// Starts a timer when something changes
public void OnFileActivity (FileSystemEventArgs args)
{
@ -373,7 +244,7 @@ namespace SparkleLib {
WatcherChangeTypes wct = args.ChangeType;
if (AnyDifferences) {
if (HasLocalChanges) {
this.is_buffering = true;
// We want to disable wathcing temporarily, but
@ -435,6 +306,44 @@ namespace SparkleLib {
}
public void AddNote (string revision, string note)
{
string notes_path = Path.Combine (LocalPath, ".notes");
if (!Directory.Exists (notes_path))
Directory.CreateDirectory (notes_path);
// Add a timestamp in seconds since unix epoch
int timestamp = (int) (DateTime.UtcNow - new DateTime (1970, 1, 1)).TotalSeconds;
string n = Environment.NewLine;
note = "<note>" + n +
" <user>" + n +
" <name>" + SparkleConfig.DefaultConfig.User.Name + "</name>" + n +
" <email>" + SparkleConfig.DefaultConfig.User.Email + "</email>" + n +
" </user>" + n +
" <timestamp>" + timestamp + "</timestamp>" + n +
" <body>" + note + "</body>" + n +
"</note>" + n;
string note_name = revision + SHA1 (timestamp.ToString () + note);
string note_path = Path.Combine (notes_path, note_name);
StreamWriter writer = new StreamWriter (note_path);
writer.Write (note);
writer.Close ();
// The watcher doesn't like .*/ so we need to trigger
// a change manually
FileSystemEventArgs args = new FileSystemEventArgs (WatcherChangeTypes.Changed,
notes_path, note_name);
OnFileActivity (args);
SparkleHelpers.DebugInfo ("Note", "Added note to " + revision);
}
private void SyncUpBase ()
{
try {
@ -556,7 +465,106 @@ namespace SparkleLib {
}
public void DisableWatching ()
private void CreateWatcher ()
{
this.watcher = new SparkleWatcher (LocalPath);
this.watcher.ChangeEvent += delegate (FileSystemEventArgs args) {
OnFileActivity (args);
};
}
private void CreateListener ()
{
this.listener = SparkleListenerFactory.CreateListener (Name, Identifier);
if (this.listener.IsConnected) {
this.poll_interval = this.long_interval;
new Thread (new ThreadStart (delegate {
if (!IsSyncing && HasRemoteChanges)
SyncDownBase ();
})).Start ();
}
// Stop polling when the connection to the irc channel is succesful
this.listener.Connected += delegate {
this.poll_interval = this.long_interval;
this.last_poll = DateTime.Now;
if (!IsSyncing) {
// Check for changes manually one more time
if (HasRemoteChanges)
SyncDownBase ();
// Push changes that were made since the last disconnect
if (HasUnsyncedChanges)
SyncUpBase ();
}
};
// Start polling when the connection to the irc channel is lost
this.listener.Disconnected += delegate {
this.poll_interval = this.short_interval;
SparkleHelpers.DebugInfo (Name, "Falling back to polling");
};
// Fetch changes when there is a message in the irc channel
this.listener.Received += delegate (SparkleAnnouncement announcement) {
string identifier = Identifier;
if (announcement.FolderIdentifier.Equals (identifier) &&
!announcement.Message.Equals (CurrentRevision)) {
while (this.IsSyncing)
System.Threading.Thread.Sleep (100);
SparkleHelpers.DebugInfo ("Listener", "Syncing due to announcement");
SyncDownBase ();
} else {
if (announcement.FolderIdentifier.Equals (identifier))
SparkleHelpers.DebugInfo ("Listener", "Not syncing, message is for current revision");
}
};
// Start listening
if (!this.listener.IsConnected && !this.listener.IsConnecting)
this.listener.Connect ();
}
private void CheckForChanges ()
{
lock (this.change_lock) {
if (this.has_changed) {
if (this.size_buffer.Count >= 4)
this.size_buffer.RemoveAt (0);
DirectoryInfo dir_info = new DirectoryInfo (LocalPath);
this.size_buffer.Add (CalculateSize (dir_info));
if (this.size_buffer.Count >= 4 &&
this.size_buffer [0].Equals (this.size_buffer [1]) &&
this.size_buffer [1].Equals (this.size_buffer [2]) &&
this.size_buffer [2].Equals (this.size_buffer [3])) {
SparkleHelpers.DebugInfo ("Local", "[" + Name + "] Changes have settled.");
this.is_buffering = false;
this.has_changed = false;
DisableWatching ();
while (HasLocalChanges)
SyncUpBase ();
EnableWatching ();
}
}
}
}
protected void DisableWatching ()
{
lock (this.watch_lock) {
this.watcher.EnableRaisingEvents = false;
@ -565,7 +573,7 @@ namespace SparkleLib {
}
public void EnableWatching ()
protected void EnableWatching ()
{
lock (this.watch_lock) {
this.watcher.EnableRaisingEvents = true;
@ -574,68 +582,6 @@ namespace SparkleLib {
}
// Create an initial change set when the
// user has fetched an empty remote folder
public virtual void CreateInitialChangeSet ()
{
string file_path = Path.Combine (LocalPath, "SparkleShare.txt");
TextWriter writer = new StreamWriter (file_path);
writer.WriteLine ("Congratulations, you've successfully created a SparkleShare repository!");
writer.WriteLine ("");
writer.WriteLine ("Any files you add or change in this folder will be automatically synced to ");
writer.WriteLine (SparkleConfig.DefaultConfig.GetUrlForFolder (Name) + " and everyone connected to it.");
// TODO: Url property? ^
writer.WriteLine ("");
writer.WriteLine ("SparkleShare is a Free and Open Source software program that helps people ");
writer.WriteLine ("collaborate and share files. If you like what we do, please consider a small ");
writer.WriteLine ("donation to support the project: http://sparkleshare.org/support-us/");
writer.WriteLine ("");
writer.WriteLine ("Have fun! :)");
writer.WriteLine ("");
writer.Close ();
}
public void AddNote (string revision, string note)
{
string notes_path = Path.Combine (LocalPath, ".notes");
if (!Directory.Exists (notes_path))
Directory.CreateDirectory (notes_path);
// Add a timestamp in seconds since unix epoch
int timestamp = (int) (DateTime.UtcNow - new DateTime (1970, 1, 1)).TotalSeconds;
string n = Environment.NewLine;
note = "<note>" + n +
" <user>" + n +
" <name>" + SparkleConfig.DefaultConfig.User.Name + "</name>" + n +
" <email>" + SparkleConfig.DefaultConfig.User.Email + "</email>" + n +
" </user>" + n +
" <timestamp>" + timestamp + "</timestamp>" + n +
" <body>" + note + "</body>" + n +
"</note>" + n;
string note_name = revision + SHA1 (timestamp.ToString () + note);
string note_path = Path.Combine (notes_path, note_name);
StreamWriter writer = new StreamWriter (note_path);
writer.Write (note);
writer.Close ();
// The watcher doesn't like .*/ so we need to trigger
// a change manually
FileSystemEventArgs args = new FileSystemEventArgs (WatcherChangeTypes.Changed,
notes_path, note_name);
OnFileActivity (args);
SparkleHelpers.DebugInfo ("Note", "Added note to " + revision);
}
private DateTime progress_last_change = DateTime.Now;
private TimeSpan progress_change_interval = new TimeSpan (0, 0, 0, 1);
@ -658,6 +604,33 @@ namespace SparkleLib {
}
// Create an initial change set when the
// user has fetched an empty remote folder
private void CreateInitialChangeSet ()
{
string file_path = Path.Combine (LocalPath, "SparkleShare.txt");
TextWriter writer = new StreamWriter (file_path);
writer.WriteLine ("Congratulations, you've successfully created a SparkleShare repository!");
writer.WriteLine ("");
writer.WriteLine ("Any files you add or change in this folder will be automatically synced to ");
writer.WriteLine (SparkleConfig.DefaultConfig.GetUrlForFolder (Name) + " and everyone connected to it.");
// TODO: Url property? ^
writer.WriteLine ("");
writer.WriteLine ("SparkleShare is a Free and Open Source software program that helps people ");
writer.WriteLine ("collaborate and share files. If you like what we do, please consider a small ");
writer.WriteLine ("donation to support the project: http://sparkleshare.org/support-us/");
writer.WriteLine ("");
writer.WriteLine ("Have fun! :)");
writer.WriteLine ("");
writer.Close ();
SyncUp ();
}
// Creates a SHA-1 hash of input
private string SHA1 (string s)
{
@ -666,5 +639,37 @@ namespace SparkleLib {
Byte[] encoded_bytes = sha1.ComputeHash (bytes);
return BitConverter.ToString (encoded_bytes).ToLower ().Replace ("-", "");
}
// Recursively gets a folder's size in bytes
private double CalculateSize (DirectoryInfo parent)
{
if (!Directory.Exists (parent.ToString ()))
return 0;
double size = 0;
// Ignore the temporary 'rebase-apply' and '.tmp' directories. This prevents potential
// crashes when files are being queried whilst the files have already been deleted.
if (ExcludePaths.Contains (parent.Name))
return 0;
try {
foreach (FileInfo file in parent.GetFiles()) {
if (!file.Exists)
return 0;
size += file.Length;
}
foreach (DirectoryInfo directory in parent.GetDirectories ())
size += CalculateSize (directory);
} catch (Exception) {
return 0;
}
return size;
}
}
}