private void EvaluateLocallyAbsentFilesAndDirs(SyncedDirectory syncedDirectory) { //evaluate the files first (needed to evaluate the dir) foreach (var syncedFile in syncedDirectory.Files.Where(x => x.LocallyAbsent && !x.RemoteDeleted).ToArray()) { //if this file was present on the last sync, it's really deleted, otherwise it is new on the remote side syncedFile.LocallyDeleted = syncedFile.TimestampOnLastSync.Local.HasValue; if (!syncedFile.LocallyDeleted) { //remote new, construct local directory syncedFile.LocalDirectory = Path.Combine(_fileRepository.StorageDirectory, syncedDirectory.GetRelativePath()); } } if (syncedDirectory.LocallyAbsent) { var shouldLive = syncedDirectory.Files.Any(x => !x.LocallyDeleted); syncedDirectory.LocallyDeleted = !shouldLive; } foreach (var subDir in syncedDirectory.SubDirectories) { EvaluateLocallyAbsentFilesAndDirs(subDir); } }
/// <summary> /// merge state from local directory into the current state. /// </summary> private static void CopyInfoFromLocalState(SyncedDirectory localState, SyncedDirectory myState) { //recursively walk the subdirectories and merge foreach (var mySubDir in myState.SubDirectories) { var localSubDirectory = localState.SubDirectories.FirstOrDefault(x => x.NameEquals(mySubDir.Name)); if (localSubDirectory == null) { //may be deleted locally, but could also be new on the remote side. //we don't know yet, this will be re-evaluated later (in this class) mySubDir.LocallyAbsent = true; localSubDirectory = new SyncedDirectory(); } CopyInfoFromLocalState(localSubDirectory, mySubDir); } //add directories that don't exist in myState but do exist in localState //these don't have to be evaluated recursively, because all info is copied immediately by adding the dirs var dirsOnlyInLocalState = localState.SubDirectories.Where(local => !myState.SubDirectories.Any(my => my.NameEquals(local.Name))); myState.AddDir(dirsOnlyInLocalState.ToArray()); //merge the files foreach (var myFile in myState.Files) { var localFile = localState.Files.FirstOrDefault(x => x.NameEquals(myFile.Name)); if (localFile == null) { //may be deleted locally, but could also be new on the remote side. //we don't know yet, this will be re-evaluated later myFile.LocallyAbsent = true; } else { myFile.CurrentSyncTimestamp.Local = localFile.CurrentSyncTimestamp.Local; myFile.LocalDirectory = localFile.LocalDirectory; } } //add local files to cloudDirState that don't exist remotely var filesOnlyInLocalState = localState.Files.Where(local => !myState.Files.Any(my => my.NameEquals(local.Name))); myState.AddFile(filesOnlyInLocalState.ToArray()); }
/// <summary> /// recursively evaluate syncstate and destill sync commands /// </summary> private IEnumerable <SyncCommand> CreateSyncCommands(SyncedDirectory dir) { var result = new List <SyncCommand>(); foreach (var subDir in dir.SubDirectories) { result.AddRange(CreateSyncCommands(subDir)); } foreach (var file in dir.Files) { if (GetSyncInfoFileName().Equals(file.Name)) { continue; } var isNewLocally = !file.LocallyAbsent && !file.TimestampOnLastSync.Local.HasValue; var isNewRemote = file.LocallyAbsent && !file.TimestampOnLastSync.Remote.HasValue; //local changes go first: remote often has a versioning system in place if (file.LocallyDeleted) { if (!file.RemoteDeleted) { result.Add(SyncCommand.DeleteRemote(file)); } } else if (file.RemoteDeleted) { if (file.LocalPath != null) //may already be deleted locally { result.Add(SyncCommand.DeleteLocal(file)); } } else if (isNewLocally || file.CurrentSyncTimestamp.Local > file.TimestampOnLastSync.Local) { result.Add(SyncCommand.Upload(file)); } else if (isNewRemote || file.CurrentSyncTimestamp.Remote > file.TimestampOnLastSync.Remote) { result.Add(SyncCommand.Download(file)); } } return(result); }
/// <summary> /// merge info from previous sync into the current state (mainly 'last sync datetime') /// </summary> private static void CopyInfoFromPreviousSync(SyncedDirectory prevState, SyncedDirectory myState) { foreach (var mySubDir in myState.SubDirectories) { var prevSubDir = prevState.SubDirectories.FirstOrDefault(x => x.NameEquals(mySubDir.Name)); prevSubDir = prevSubDir ?? new SyncedDirectory(); CopyInfoFromPreviousSync(prevSubDir, mySubDir); } foreach (var myFile in myState.Files) { var prevFile = prevState.Files.FirstOrDefault(x => x.NameEquals(myFile.Name)); if (prevFile != null) { myFile.TimestampOnLastSync = prevFile.TimestampAfterLastSync; } } }
/// <summary> /// merge syncinfo from after sync to the properties in the syncinfo /// </summary> /// <param name="inputForSync">syncinfo before and during sync</param> /// <param name="syncStateAfterTheFact">syncinfo after sync</param> private void CopyCurrentTimestampsToAfterLastSyncFrom(SyncedDirectory inputForSync, SyncedDirectory syncStateAfterTheFact) { foreach (var inputDir in inputForSync.SubDirectories) { var dirAfterTheFact = syncStateAfterTheFact.SubDirectories.FirstOrDefault(x => x.NameEquals(inputDir.Name)); dirAfterTheFact = dirAfterTheFact ?? new SyncedDirectory(); //apparently removed during sync by another app or manually CopyCurrentTimestampsToAfterLastSyncFrom(inputDir, dirAfterTheFact); } foreach (var inputFile in inputForSync.Files) { var fileAfterTheFact = syncStateAfterTheFact.Files.FirstOrDefault(x => x.NameEquals(inputFile.Name)); if (fileAfterTheFact != null) { inputFile.TimestampAfterLastSync = fileAfterTheFact.CurrentSyncTimestamp; } } }
/// <summary> /// restore sync info from previous sync from disk /// </summary> private SyncedDirectory GetPrevSyncInfo() { var prevSyncInfoText = _fileRepository.GetText(GetSyncInfoFileName()); var prevSyncInfo = new SyncedDirectory(); if (!string.IsNullOrEmpty(prevSyncInfoText)) { try { prevSyncInfo = JsonConvert.DeserializeObject <SyncedDirectory>(prevSyncInfoText); } catch { //file is apparently broken: all sync information is lost. //will be overwritten with new information. } } return(prevSyncInfo); }
/// <summary> /// restore sync info from previous sync from disk /// </summary> private SyncedDirectory GetPrevSyncInfo() { var prevSyncInfoText = _fileRepository.GetText(GetSyncInfoFileName()); var prevSyncInfo = new SyncedDirectory(); if (!string.IsNullOrEmpty(prevSyncInfoText)) { try { prevSyncInfo = JsonConvert.DeserializeObject<SyncedDirectory>(prevSyncInfoText); } catch { //file is apparently broken: all sync information is lost. //will be overwritten with new information. } } return prevSyncInfo; }
/// <summary> /// Synchronize all files to and from Dropbox /// </summary> public async Task Initialize() { _syncState = await BuildUpSyncState(); }
public void CopyInfoFromLocalState(SyncedDirectory localState) { CopyInfoFromLocalState(localState, this); }
private SyncedDirectory GetLocalSyncState(DirectoryInfo dir) { var result = new SyncedDirectory { Name = dir.Name }; foreach (var subDir in dir.GetDirectories()) { result.AddDir(GetLocalSyncState(subDir)); } foreach (var file in dir.GetFiles()) { result.AddFile(new SyncedFile { Name = file.Name, LocalDirectory = file.DirectoryName, CurrentSyncTimestamp = { Local = file.LastWriteTimeUtc } }); } return result; }
public void CopyInfoFromPreviousSync(SyncedDirectory prevState) { CopyInfoFromPreviousSync(prevState, this); }
/// <summary> /// recursively evaluate syncstate and destill sync commands /// </summary> private IEnumerable<SyncCommand> CreateSyncCommands(SyncedDirectory dir) { var result = new List<SyncCommand>(); foreach (var subDir in dir.SubDirectories) { result.AddRange(CreateSyncCommands(subDir)); } foreach (var file in dir.Files) { if (GetSyncInfoFileName().Equals(file.Name)) { continue; } var isNewLocally = !file.LocallyAbsent && !file.TimestampOnLastSync.Local.HasValue; var isNewRemote = file.LocallyAbsent && !file.TimestampOnLastSync.Remote.HasValue; //local changes go first: remote often has a versioning system in place if (file.LocallyDeleted) { if (!file.RemoteDeleted) { result.Add(SyncCommand.DeleteRemote(file)); } } else if (file.RemoteDeleted) { if (file.LocalPath != null) //may already be deleted locally { result.Add(SyncCommand.DeleteLocal(file)); } } else if (isNewLocally || file.CurrentSyncTimestamp.Local > file.TimestampOnLastSync.Local) { result.Add(SyncCommand.Upload(file)); } else if (isNewRemote || file.CurrentSyncTimestamp.Remote > file.TimestampOnLastSync.Remote) { result.Add(SyncCommand.Download(file)); } } return result; }