using System; using System.Diagnostics.Contracts; using System.IO; using System.Threading; using Pithos.Interfaces; using Pithos.Network; namespace Pithos.Core.Agents { public enum CloudActionType { MustSynch, UploadUnconditional, DownloadUnconditional, DeleteLocal, DeleteCloud, RenameCloud } public class CloudAction { public AccountInfo AccountInfo { get; set; } public CloudActionType Action { get; set; } public FileSystemInfo LocalFile { get; set; } public ObjectInfo CloudFile { get; set; } public FileState FileState { get; set; } public string Container { get; set; } public readonly DateTime Created = DateTime.Now; public Lazy LocalHash { get; protected set; } private Lazy _topHash; public Lazy TopHash { get { return _topHash; } set { _topHash = value; } } [ContractInvariantMethod] private void Invariants() { Contract.Invariant(AccountInfo!=null); } public bool IsShared { get { return CloudFile!=null && AccountInfo.UserName != CloudFile.Account; } } protected CloudAction(AccountInfo accountInfo,CloudActionType action) { if (accountInfo==null) throw new ArgumentNullException("accountInfo"); Contract.EndContractBlock(); Action = action; AccountInfo = accountInfo; } public CloudAction(AccountInfo accountInfo, CloudActionType action, FileSystemInfo localFile, ObjectInfo cloudFile, FileState state, int blockSize, string algorithm) : this(accountInfo,action) { LocalFile = localFile.WithProperCapitalization(); CloudFile = cloudFile; FileState = state; if (LocalFile != null) { LocalHash = new Lazy(() => LocalFile.CalculateHash(blockSize,algorithm), LazyThreadSafetyMode.ExecutionAndPublication); } } //Calculate the download path for the cloud file public string GetDownloadPath() { if (CloudFile == null) return String.Empty; var filePath = CloudFile.RelativeUrlToFilePath(AccountInfo.UserName); return Path.Combine(AccountInfo.AccountPath, filePath); } public override string ToString() { return String.Format("{0}:{1}->{2}", this.Action, this.LocalFile.FullName, this.CloudFile.Name); } protected static ObjectInfo CreateObjectInfoFor(AccountInfo accountInfo, FileSystemInfo fileInfo) { Contract.Requires(accountInfo!=null); Contract.Requires(fileInfo!=null); Contract.Ensures(Contract.Result()!=null); var capitalizedFileInfo = fileInfo.WithProperCapitalization(); var fullLocalName = capitalizedFileInfo.FullName; var othersPath = Path.Combine(accountInfo.AccountPath, FolderConstants.OthersFolder); var isShared = fullLocalName.StartsWith(othersPath, StringComparison.InvariantCultureIgnoreCase); if (isShared) { var pathRelativeToOther = fullLocalName.Substring(othersPath.Length + 1); var otherParts = pathRelativeToOther.Split('\\'); var otherName = otherParts[0]; var otherContainer = otherParts[1]; return new ObjectInfo { Account = otherName, Container = otherContainer, Name = String.Join("/", otherParts.Splice(2)) }; } return new ObjectInfo(accountInfo.AccountPath, accountInfo.UserName, fileInfo); } } public class CloudDownloadAction:CloudAction { public CloudDownloadAction(AccountInfo accountInfo, ObjectInfo cloudFile) :base(accountInfo,CloudActionType.DownloadUnconditional) { if (String.IsNullOrWhiteSpace(cloudFile.Container)) throw new ArgumentException("CloudFile.Container","cloudFile"); Contract.EndContractBlock(); CloudFile = cloudFile; } [ContractInvariantMethod] private void Invariants() { Contract.Invariant(!String.IsNullOrWhiteSpace(CloudFile.Container)); } public override string ToString() { return String.Format("{0}: _ <- {1}", this.Action, this.CloudFile.Name); } } public class CloudDeleteAction:CloudAction { public CloudDeleteAction(AccountInfo accountInfo,FileSystemInfo fileInfo, FileState fileState) : this(accountInfo,fileInfo,CreateObjectInfoFor(accountInfo, fileInfo),fileState) { } public CloudDeleteAction(AccountInfo accountInfo, FileSystemInfo fileInfo,ObjectInfo cloudFile, FileState fileState) : base(accountInfo,CloudActionType.DeleteCloud) { CloudFile = cloudFile; LocalFile = fileInfo; FileState = fileState; } public CloudDeleteAction(CloudAction action) : this(action.AccountInfo,action.LocalFile,action.CloudFile,action.FileState) {} [ContractInvariantMethod] private void Invariants() { Contract.Invariant(!String.IsNullOrWhiteSpace(CloudFile.Container)); } public override string ToString() { return String.Format("{0}: _ ->{1}", this.Action, this.CloudFile.Name); } } public class CloudUploadAction:CloudAction { public CloudUploadAction(AccountInfo accountInfo, FileSystemInfo fileInfo, FileState state, int blockSize, string algorithm) : base(accountInfo, CloudActionType.UploadUnconditional,fileInfo,CreateObjectInfoFor(accountInfo,fileInfo),state,blockSize,algorithm) { } [ContractInvariantMethod] private void Invariants() { Contract.Invariant(!String.IsNullOrWhiteSpace(CloudFile.Container)); } } public class CloudMoveAction:CloudAction { public ObjectInfo OldCloudFile { get; set; } public FileSystemInfo OldLocalFile { get; set; } public CloudMoveAction(AccountInfo accountInfo, CloudActionType action, FileSystemInfo oldFile, FileSystemInfo newFile) :base(accountInfo,action) { LocalFile = newFile; CloudFile = CreateObjectInfoFor(accountInfo, newFile); OldLocalFile = oldFile; OldCloudFile = CreateObjectInfoFor(accountInfo, oldFile); //This is a rename operation, a hash will not be used LocalHash = new Lazy(() => String.Empty, LazyThreadSafetyMode.ExecutionAndPublication); } public override string ToString() { return String.Format("{0}:{1}->{2}", this.Action, OldCloudFile.Name, CloudFile.Name); } } }