diff --git a/PCLExt.FileStorage.sln b/PCLExt.FileStorage.sln index 88de1c2..c552ffb 100644 --- a/PCLExt.FileStorage.sln +++ b/PCLExt.FileStorage.sln @@ -3,9 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2012 VisualStudioVersion = 14.0.25123.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCLExt.FileStorage", "src\PCLExt.FileStorage\PCLExt.FileStorage.csproj", "{A7C97A2B-F996-4CAF-87D8-F3A60B2B4D44}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCLExt.FileStorage.Portable", "src\PCLExt.FileStorage.Portable\PCLExt.FileStorage.Portable.csproj", "{A7C97A2B-F996-4CAF-87D8-F3A60B2B4D44}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCLExt.FileStorage.Abstractions", "src\PCLExt.FileStorage.Abstractions\PCLExt.FileStorage.Abstractions.csproj", "{3F8D0494-6EE2-47FE-B263-F09A5EA3D0B3}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCLExt.FileStorage.Portable.Abstractions", "src\PCLExt.FileStorage.Portable.Abstractions\PCLExt.FileStorage.Portable.Abstractions.csproj", "{3F8D0494-6EE2-47FE-B263-F09A5EA3D0B3}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCLExt.FileStorage.Desktop", "src\PCLExt.FileStorage.Desktop\PCLExt.FileStorage.Desktop.csproj", "{54DBACEB-B898-494B-AD88-2A407CB55A5A}" EndProject @@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCLExt.FileStorage.iOS", "s EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCLExt.FileStorage.Mac", "src\PCLExt.FileStorage.Mac\PCLExt.FileStorage.Mac.csproj", "{0B02DE9A-C3C5-4DB9-B87F-6B05C3566946}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCLExt.FileStorage.Core", "src\PCLExt.FileStorage.Core\PCLExt.FileStorage.Core.csproj", "{94E4FE17-7F2B-4623-8FFA-12F4056CCB2A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution AppStore|Any CPU = AppStore|Any CPU @@ -22,18 +24,12 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {03DA3968-61A3-44D1-9F99-F55D31E07813}.AppStore|Any CPU.ActiveCfg = AppStore|Any CPU - {03DA3968-61A3-44D1-9F99-F55D31E07813}.AppStore|Any CPU.Build.0 = AppStore|Any CPU - {03DA3968-61A3-44D1-9F99-F55D31E07813}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {03DA3968-61A3-44D1-9F99-F55D31E07813}.Debug|Any CPU.Build.0 = Debug|Any CPU - {03DA3968-61A3-44D1-9F99-F55D31E07813}.Release|Any CPU.ActiveCfg = Release|Any CPU - {03DA3968-61A3-44D1-9F99-F55D31E07813}.Release|Any CPU.Build.0 = Release|Any CPU - {0B02DE9A-C3C5-4DB9-B87F-6B05C3566946}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {0B02DE9A-C3C5-4DB9-B87F-6B05C3566946}.AppStore|Any CPU.Build.0 = Release|Any CPU - {0B02DE9A-C3C5-4DB9-B87F-6B05C3566946}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0B02DE9A-C3C5-4DB9-B87F-6B05C3566946}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0B02DE9A-C3C5-4DB9-B87F-6B05C3566946}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0B02DE9A-C3C5-4DB9-B87F-6B05C3566946}.Release|Any CPU.Build.0 = Release|Any CPU + {A7C97A2B-F996-4CAF-87D8-F3A60B2B4D44}.AppStore|Any CPU.ActiveCfg = Release|Any CPU + {A7C97A2B-F996-4CAF-87D8-F3A60B2B4D44}.AppStore|Any CPU.Build.0 = Release|Any CPU + {A7C97A2B-F996-4CAF-87D8-F3A60B2B4D44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A7C97A2B-F996-4CAF-87D8-F3A60B2B4D44}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7C97A2B-F996-4CAF-87D8-F3A60B2B4D44}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A7C97A2B-F996-4CAF-87D8-F3A60B2B4D44}.Release|Any CPU.Build.0 = Release|Any CPU {3F8D0494-6EE2-47FE-B263-F09A5EA3D0B3}.AppStore|Any CPU.ActiveCfg = Release|Any CPU {3F8D0494-6EE2-47FE-B263-F09A5EA3D0B3}.AppStore|Any CPU.Build.0 = Release|Any CPU {3F8D0494-6EE2-47FE-B263-F09A5EA3D0B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -52,12 +48,24 @@ Global {8EFC0559-3C9C-4D0C-A5A5-579E58FFE621}.Debug|Any CPU.Build.0 = Debug|Any CPU {8EFC0559-3C9C-4D0C-A5A5-579E58FFE621}.Release|Any CPU.ActiveCfg = Release|Any CPU {8EFC0559-3C9C-4D0C-A5A5-579E58FFE621}.Release|Any CPU.Build.0 = Release|Any CPU - {A7C97A2B-F996-4CAF-87D8-F3A60B2B4D44}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {A7C97A2B-F996-4CAF-87D8-F3A60B2B4D44}.AppStore|Any CPU.Build.0 = Release|Any CPU - {A7C97A2B-F996-4CAF-87D8-F3A60B2B4D44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A7C97A2B-F996-4CAF-87D8-F3A60B2B4D44}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A7C97A2B-F996-4CAF-87D8-F3A60B2B4D44}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A7C97A2B-F996-4CAF-87D8-F3A60B2B4D44}.Release|Any CPU.Build.0 = Release|Any CPU + {03DA3968-61A3-44D1-9F99-F55D31E07813}.AppStore|Any CPU.ActiveCfg = AppStore|Any CPU + {03DA3968-61A3-44D1-9F99-F55D31E07813}.AppStore|Any CPU.Build.0 = AppStore|Any CPU + {03DA3968-61A3-44D1-9F99-F55D31E07813}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {03DA3968-61A3-44D1-9F99-F55D31E07813}.Debug|Any CPU.Build.0 = Debug|Any CPU + {03DA3968-61A3-44D1-9F99-F55D31E07813}.Release|Any CPU.ActiveCfg = Release|Any CPU + {03DA3968-61A3-44D1-9F99-F55D31E07813}.Release|Any CPU.Build.0 = Release|Any CPU + {0B02DE9A-C3C5-4DB9-B87F-6B05C3566946}.AppStore|Any CPU.ActiveCfg = Release|Any CPU + {0B02DE9A-C3C5-4DB9-B87F-6B05C3566946}.AppStore|Any CPU.Build.0 = Release|Any CPU + {0B02DE9A-C3C5-4DB9-B87F-6B05C3566946}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0B02DE9A-C3C5-4DB9-B87F-6B05C3566946}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0B02DE9A-C3C5-4DB9-B87F-6B05C3566946}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0B02DE9A-C3C5-4DB9-B87F-6B05C3566946}.Release|Any CPU.Build.0 = Release|Any CPU + {94E4FE17-7F2B-4623-8FFA-12F4056CCB2A}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {94E4FE17-7F2B-4623-8FFA-12F4056CCB2A}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {94E4FE17-7F2B-4623-8FFA-12F4056CCB2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {94E4FE17-7F2B-4623-8FFA-12F4056CCB2A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {94E4FE17-7F2B-4623-8FFA-12F4056CCB2A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {94E4FE17-7F2B-4623-8FFA-12F4056CCB2A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/common/CommonAssemblyInfo.cs b/common/CommonAssemblyInfo.cs index e4944f5..12e9aab 100644 --- a/common/CommonAssemblyInfo.cs +++ b/common/CommonAssemblyInfo.cs @@ -3,9 +3,9 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("PCLExt.FileStorage")] -[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyCopyright("Copyright © 2016-2017")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("1.1.3.0")] -[assembly: AssemblyFileVersion("1.1.3.0")] +[assembly: AssemblyVersion("1.2.0.0")] +[assembly: AssemblyFileVersion("1.2.0.0")] diff --git a/common/PCLExt.FileStorage.nuspec b/common/PCLExt.FileStorage.nuspec index 962482b..c36651c 100644 --- a/common/PCLExt.FileStorage.nuspec +++ b/common/PCLExt.FileStorage.nuspec @@ -2,7 +2,7 @@ PCLExt.FileStorage - 1.1.3.0 + 1.2.0.0 PCL Extension - File Storage API Daniel Plaisted,Aragas Aragas @@ -10,30 +10,37 @@ https://github.com/Aragas/PCLExt.FileStorage https://raw.githubusercontent.com/Aragas/PCLExt.FileStorage/master/common/sushi_64.png false - PCL Extension provides file system implementations for PCL, .NET, Xamarin.iOS, Xamarin.Android and Xamarin.Mac. This makes it easier to create cross-platform .NET libraries and apps. + PCL Extension provides file system implementations for PCL, .NET, Xamarin.iOS, Xamarin.Android and Xamarin.Mac. This makes it easier to create cross-platform .NET libraries and apps. Supports .NET Standard now. PCL Extension provides file system implementations for PCL platform - io storage file system portable pcl android mac ios xamarin monoandroid monotouch + io storage file system portable pcl android mac ios xamarin monoandroid monotouch core netcore netstandard - - - - - - + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/PCLExt.FileStorage.Abstractions/IFileSystem.cs b/src/PCLExt.FileStorage.Abstractions/IFileSystem.cs deleted file mode 100644 index f7036f8..0000000 --- a/src/PCLExt.FileStorage.Abstractions/IFileSystem.cs +++ /dev/null @@ -1,34 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Daniel Plaisted. All rights reserved. -// -// This file is a derivation of: -// https://github.com/dsplaisted/PCLStorage -// Which is released under the MS-PL license. -//----------------------------------------------------------------------- - -namespace PCLExt.FileStorage -{ - /// - /// Represents a file system. - /// - public interface IFileSystem - { - /// - /// A folder representing storage which is where the app is running. - /// - IFolder BaseStorage { get; } - /// - /// A folder representing storage which is local to the current device. - /// - IFolder LocalStorage { get; } - /// - /// A folder representing storage which may be synced with other devices for the same user. - /// - IFolder RoamingStorage { get; } - /// - /// Depending on OS, it will return BaseStorage on Desktop platforms and LocalStorage on Mobile platforms. - /// - IFolder SpecialStorage { get; } - } -} diff --git a/src/PCLExt.FileStorage.Android/PCLExt.FileStorage.Android.csproj b/src/PCLExt.FileStorage.Android/PCLExt.FileStorage.Android.csproj index c972a28..bae5917 100644 --- a/src/PCLExt.FileStorage.Android/PCLExt.FileStorage.Android.csproj +++ b/src/PCLExt.FileStorage.Android/PCLExt.FileStorage.Android.csproj @@ -37,44 +37,84 @@ - - - + Properties\CommonAssemblyInfo.cs - - DesktopFileSystem.cs + + NET4FileImplementation.cs - - FileSystemFile.cs + + NET4FolderImplementation.cs - - FileSystemFolder.cs + + BaseFile.cs - - AwaitExtensions.cs + + BaseFolder.cs - - PortablePath.cs + + CreationCollisionOption.cs - - Requires.cs + + ExistenceCheckResult.cs - - BaseFolder.cs + + FolderSearchOption.cs - - BaseFile.cs + + IFile.cs - - - FileSystem.cs + + IFolder.cs + + + NameCollisionOption.cs + + + Exceptions\DirectoryNotFoundException.cs + + + Exceptions\ExceptionsHelper.cs + + + Exceptions\FileNotFoundException.cs + + + Extensions\AwaitExtensions.cs + + Extensions\FileExtensions.cs + + + Extensions\FolderExtensions.cs + + + Files\FileFromPath.cs + + + Folders\ApplicationFolder.cs + + + Folders\FolderFromPath.cs + + + Folders\LocalFolder.cs + + + Folders\RoamingFolder.cs + + + PortablePath.cs + + + Requires.cs + + - + {3F8D0494-6EE2-47FE-B263-F09A5EA3D0B3} PCLExt.FileStorage.Abstractions diff --git a/src/PCLExt.FileStorage.Core/NETCOREFileImplementation.cs b/src/PCLExt.FileStorage.Core/NETCOREFileImplementation.cs new file mode 100644 index 0000000..5400d50 --- /dev/null +++ b/src/PCLExt.FileStorage.Core/NETCOREFileImplementation.cs @@ -0,0 +1,252 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Daniel Plaisted. All rights reserved. +// +// This file is a derivation of: +// https://github.com/dsplaisted/PCLStorage +// Which is released under the MS-PL license. +//----------------------------------------------------------------------- + +using System; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +using PCLExt.FileStorage.Extensions; + +namespace PCLExt.FileStorage +{ + /// + /// Represents a file in the . + /// + [DebuggerDisplay("Name = {" + nameof(Name) + "}")] + internal class NETCOREFileImplementation : IFile + { + /// + public string Name { get; private set; } + /// + public string Path { get; private set; } + + /// + /// Creates a new corresponding to the specified path. + /// + /// The file path + public NETCOREFileImplementation(string path) + { + Name = System.IO.Path.GetFileName(path); + Path = path; + } + + /// + public Stream Open(FileAccess fileAccess) + { + if (fileAccess == FileAccess.Read) + return File.OpenRead(Path); + else if (fileAccess == FileAccess.ReadAndWrite) + return File.Open(Path, FileMode.Open, System.IO.FileAccess.ReadWrite); + else + throw new ArgumentException($"Unrecognized FileAccess value: {fileAccess}"); + } + /// + public async Task OpenAsync(FileAccess fileAccess, CancellationToken cancellationToken) + { + await AwaitExtensions.SwitchOffMainThreadAsync(cancellationToken); + + if (fileAccess == FileAccess.Read) + return File.OpenRead(Path); + else if (fileAccess == FileAccess.ReadAndWrite) + return File.Open(Path, FileMode.Open, System.IO.FileAccess.ReadWrite); + else + throw new ArgumentException($"Unrecognized FileAccess value: {fileAccess}"); + } + + /// + public void Delete() + { + if (!File.Exists(Path)) + throw new FileNotFoundException($"File does not exist: {Path}"); + + File.Delete(Path); + } + /// + public async Task DeleteAsync(CancellationToken cancellationToken) + { + await AwaitExtensions.SwitchOffMainThreadAsync(cancellationToken); + + if (!File.Exists(Path)) + throw new FileNotFoundException($"File does not exist: {Path}"); + + File.Delete(Path); + } + + /// + public void Rename(string newName, NameCollisionOption collisionOption) + { + Requires.NotNullOrEmpty(newName, "newName"); + + Move(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(Path), newName), collisionOption); + } + /// + public async Task RenameAsync(string newName, NameCollisionOption collisionOption, CancellationToken cancellationToken) + { + Requires.NotNullOrEmpty(newName, "newName"); + + await AwaitExtensions.SwitchOffMainThreadAsync(cancellationToken); + + await MoveAsync(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(Path), newName), collisionOption, cancellationToken); + } + + /// + public void Move(string newPath, NameCollisionOption collisionOption) + { + Requires.NotNullOrEmpty(newPath, "newPath"); + + var newDirectory = System.IO.Path.GetDirectoryName(newPath); + var newName = System.IO.Path.GetFileName(newPath); + + for (var counter = 1; ; counter++) + { + var candidateName = newName; + if (counter > 1) + candidateName = $"{System.IO.Path.GetFileNameWithoutExtension(newName)} ({counter}){System.IO.Path.GetExtension(newName)}"; + + var candidatePath = System.IO.Path.Combine(newDirectory, candidateName); + + if (File.Exists(candidatePath)) + { + switch (collisionOption) + { + case NameCollisionOption.FailIfExists: + throw new IOException("File already exists."); + case NameCollisionOption.GenerateUniqueName: + continue; // try again with a new name. + case NameCollisionOption.ReplaceExisting: + File.Delete(candidatePath); + break; + } + } + + File.Move(Path, candidatePath); + Path = candidatePath; + Name = candidateName; + return; + } + } + /// + public async Task MoveAsync(string newPath, NameCollisionOption collisionOption, CancellationToken cancellationToken) + { + Requires.NotNullOrEmpty(newPath, "newPath"); + + await AwaitExtensions.SwitchOffMainThreadAsync(cancellationToken); + + var newDirectory = System.IO.Path.GetDirectoryName(newPath); + var newName = System.IO.Path.GetFileName(newPath); + + for (var counter = 1; ; counter++) + { + cancellationToken.ThrowIfCancellationRequested(); + var candidateName = newName; + if (counter > 1) + candidateName = $"{System.IO.Path.GetFileNameWithoutExtension(newName)} ({counter}){System.IO.Path.GetExtension(newName)}"; + + var candidatePath = System.IO.Path.Combine(newDirectory, candidateName); + + if (File.Exists(candidatePath)) + { + switch (collisionOption) + { + case NameCollisionOption.FailIfExists: + throw new IOException("File already exists."); + case NameCollisionOption.GenerateUniqueName: + continue; // try again with a new name. + case NameCollisionOption.ReplaceExisting: + File.Delete(candidatePath); + break; + } + } + + File.Move(Path, candidatePath); + Path = candidatePath; + Name = candidateName; + return; + } + } + + /// + public void Copy(string newPath, NameCollisionOption collisionOption) + { + Requires.NotNullOrEmpty(newPath, "newPath"); + + var newDirectory = System.IO.Path.GetDirectoryName(newPath); + var newName = System.IO.Path.GetFileName(newPath); + + for (var counter = 1; ; counter++) + { + var candidateName = newName; + if (counter > 1) + candidateName = $"{System.IO.Path.GetFileNameWithoutExtension(newName)} ({counter}){System.IO.Path.GetExtension(newName)}"; + + var candidatePath = System.IO.Path.Combine(newDirectory, candidateName); + + if (File.Exists(candidatePath)) + { + switch (collisionOption) + { + case NameCollisionOption.FailIfExists: + throw new IOException("File already exists."); + case NameCollisionOption.GenerateUniqueName: + continue; // try again with a new name. + case NameCollisionOption.ReplaceExisting: + File.Delete(candidatePath); + break; + } + } + + File.Copy(Path, candidatePath); + Path = candidatePath; + Name = candidateName; + return; + } + } + /// + public async Task CopyAsync(string newPath, NameCollisionOption collisionOption, CancellationToken cancellationToken) + { + Requires.NotNullOrEmpty(newPath, "newPath"); + + await AwaitExtensions.SwitchOffMainThreadAsync(cancellationToken); + + var newDirectory = System.IO.Path.GetDirectoryName(newPath); + var newName = System.IO.Path.GetFileName(newPath); + + for (var counter = 1; ; counter++) + { + cancellationToken.ThrowIfCancellationRequested(); + var candidateName = newName; + if (counter > 1) + candidateName = $"{System.IO.Path.GetFileNameWithoutExtension(newName)} ({counter}){System.IO.Path.GetExtension(newName)}"; + + var candidatePath = System.IO.Path.Combine(newDirectory, candidateName); + + if (File.Exists(candidatePath)) + { + switch (collisionOption) + { + case NameCollisionOption.FailIfExists: + throw new IOException("File already exists."); + case NameCollisionOption.GenerateUniqueName: + continue; // try again with a new name. + case NameCollisionOption.ReplaceExisting: + File.Delete(candidatePath); + break; + } + } + + File.Copy(Path, candidatePath); + Path = candidatePath; + Name = candidateName; + return; + } + } + } +} diff --git a/src/PCLExt.FileStorage.Core/NETCOREFolderImplementation.cs b/src/PCLExt.FileStorage.Core/NETCOREFolderImplementation.cs new file mode 100644 index 0000000..8514e0c --- /dev/null +++ b/src/PCLExt.FileStorage.Core/NETCOREFolderImplementation.cs @@ -0,0 +1,386 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Daniel Plaisted. All rights reserved. +// +// This file is a derivation of: +// https://github.com/dsplaisted/PCLStorage +// Which is released under the MS-PL license. +//----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +using PCLExt.FileStorage.Extensions; + +namespace PCLExt.FileStorage +{ + /// + /// Represents a folder in the . + /// + [DebuggerDisplay("Name = {" + nameof(Name) + "}")] + internal class NETCOREFolderImplementation : IFolder + { + private readonly bool _canDelete; + + /// + public string Name { get; } + /// + public string Path { get; } + + /// + /// Creates a new corresponding to a specified path. + /// + /// The folder path. + /// Specifies whether the folder can be deleted (via ). + public NETCOREFolderImplementation(string path, bool canDelete = false) { Name = System.IO.Path.GetFileName(path); Path = path; _canDelete = canDelete; } + + /// + public IFile CreateFile(string desiredName, CreationCollisionOption option) + { + Requires.NotNullOrEmpty(desiredName, nameof(desiredName)); + + EnsureExists(); + + var nameToUse = desiredName; + var newPath = System.IO.Path.Combine(Path, nameToUse); + if (File.Exists(newPath)) + { + if (option == CreationCollisionOption.GenerateUniqueName) + { + var desiredRoot = System.IO.Path.GetFileNameWithoutExtension(desiredName); + var desiredExtension = System.IO.Path.GetExtension(desiredName); + for (var num = 2; File.Exists(newPath); num++) + { + nameToUse = $"{desiredRoot} ({num}){desiredExtension}"; + newPath = System.IO.Path.Combine(Path, nameToUse); + } + InternalCreateFile(newPath); + } + else if (option == CreationCollisionOption.ReplaceExisting) + { + File.Delete(newPath); + InternalCreateFile(newPath); + } + else if (option == CreationCollisionOption.FailIfExists) + throw new IOException($"File already exists: {newPath}"); + else if (option == CreationCollisionOption.OpenIfExists) + { + // No operation. + } + else + throw new ArgumentException($"Unrecognized CreationCollisionOption: {option}"); + } + else + { + // Create file. + InternalCreateFile(newPath); + } + + return new NETCOREFileImplementation(newPath); + } + /// + public async Task CreateFileAsync(string desiredName, CreationCollisionOption option, CancellationToken cancellationToken = default(CancellationToken)) + { + Requires.NotNullOrEmpty(desiredName, nameof(desiredName)); + + await AwaitExtensions.SwitchOffMainThreadAsync(cancellationToken); + EnsureExists(); + + var nameToUse = desiredName; + var newPath = System.IO.Path.Combine(Path, nameToUse); + if (File.Exists(newPath)) + { + if (option == CreationCollisionOption.GenerateUniqueName) + { + var desiredRoot = System.IO.Path.GetFileNameWithoutExtension(desiredName); + var desiredExtension = System.IO.Path.GetExtension(desiredName); + for (var num = 2; File.Exists(newPath); num++) + { + cancellationToken.ThrowIfCancellationRequested(); + nameToUse = $"{desiredRoot} ({num}){desiredExtension}"; + newPath = System.IO.Path.Combine(Path, nameToUse); + } + InternalCreateFile(newPath); + } + else if (option == CreationCollisionOption.ReplaceExisting) + { + File.Delete(newPath); + InternalCreateFile(newPath); + } + else if (option == CreationCollisionOption.FailIfExists) + throw new IOException($"File already exists: {newPath}"); + else if (option == CreationCollisionOption.OpenIfExists) + { + // No operation. + } + else + throw new ArgumentException($"Unrecognized CreationCollisionOption: {option}"); + } + else + { + // Create file. + InternalCreateFile(newPath); + } + + return new NETCOREFileImplementation(newPath); + } + void InternalCreateFile(string path) + { + using (var stream = File.Create(path)) { } + } + + /// + public IFile GetFile(string name) + { + var path = System.IO.Path.Combine(Path, name); + if (!File.Exists(path)) + throw new FileNotFoundException($"File does not exist: {path}"); + return new NETCOREFileImplementation(path); + } + /// + public async Task GetFileAsync(string name, CancellationToken cancellationToken = default(CancellationToken)) + { + await AwaitExtensions.SwitchOffMainThreadAsync(cancellationToken); + + var path = System.IO.Path.Combine(Path, name); + if (!File.Exists(path)) + throw new FileNotFoundException($"File does not exist: {path}"); + return new NETCOREFileImplementation(path); + } + + /// + public IList GetFiles(string searchPattern = "*", FolderSearchOption searchOption = FolderSearchOption.TopFolderOnly) + { + EnsureExists(); + return Directory.GetFiles(Path, searchPattern, (SearchOption) searchOption).Select(f => new NETCOREFileImplementation(f)).ToList().AsReadOnly(); + } + /// + public async Task> GetFilesAsync(string searchPattern = "*", FolderSearchOption searchOption = FolderSearchOption.TopFolderOnly, CancellationToken cancellationToken = default(CancellationToken)) + { + await AwaitExtensions.SwitchOffMainThreadAsync(cancellationToken); + + EnsureExists(); + return Directory.GetFiles(Path, searchPattern, (SearchOption) searchOption).Select(f => new NETCOREFileImplementation(f)).ToList().AsReadOnly(); + } + + /// + public IFolder CreateFolder(string desiredName, CreationCollisionOption option) + { + Requires.NotNullOrEmpty(desiredName, nameof(desiredName)); + + EnsureExists(); + var nameToUse = desiredName; + var newPath = System.IO.Path.Combine(Path, nameToUse); + if (Directory.Exists(newPath)) + { + if (option == CreationCollisionOption.GenerateUniqueName) + { + for (var num = 2; Directory.Exists(newPath); num++) + { + nameToUse = $"{desiredName} ({num})"; + newPath = System.IO.Path.Combine(Path, nameToUse); + } + Directory.CreateDirectory(newPath); + } + else if (option == CreationCollisionOption.ReplaceExisting) + { + Directory.Delete(newPath, true); + Directory.CreateDirectory(newPath); + } + else if (option == CreationCollisionOption.FailIfExists) + throw new IOException($"Directory already exists: {newPath}"); + + else if (option == CreationCollisionOption.OpenIfExists) + { + // No operation. + } + else + throw new ArgumentException($"Unrecognized CreationCollisionOption: {option}"); + } + else + Directory.CreateDirectory(newPath); + + return new NETCOREFolderImplementation(newPath, true); + } + /// + public async Task CreateFolderAsync(string desiredName, CreationCollisionOption option, CancellationToken cancellationToken = default(CancellationToken)) + { + Requires.NotNullOrEmpty(desiredName, nameof(desiredName)); + + await AwaitExtensions.SwitchOffMainThreadAsync(cancellationToken); + + EnsureExists(); + var nameToUse = desiredName; + var newPath = System.IO.Path.Combine(Path, nameToUse); + if (Directory.Exists(newPath)) + { + if (option == CreationCollisionOption.GenerateUniqueName) + { + for (var num = 2; Directory.Exists(newPath); num++) + { + cancellationToken.ThrowIfCancellationRequested(); + nameToUse = $"{desiredName} ({num})"; + newPath = System.IO.Path.Combine(Path, nameToUse); + } + Directory.CreateDirectory(newPath); + } + else if (option == CreationCollisionOption.ReplaceExisting) + { + Directory.Delete(newPath, true); + Directory.CreateDirectory(newPath); + } + else if (option == CreationCollisionOption.FailIfExists) + throw new IOException($"Directory already exists: {newPath}"); + + else if (option == CreationCollisionOption.OpenIfExists) + { + // No operation. + } + else + throw new ArgumentException($"Unrecognized CreationCollisionOption: {option}"); + } + else + Directory.CreateDirectory(newPath); + + return new NETCOREFolderImplementation(newPath, true); + } + + /// + public IFolder GetFolder(string name) + { + Requires.NotNullOrEmpty(name, nameof(name)); + + var path = System.IO.Path.Combine(Path, name); + if (!Directory.Exists(path)) + throw new DirectoryNotFoundException($"Directory does not exist: {path}"); + return new NETCOREFolderImplementation(path, true); + } + /// + public async Task GetFolderAsync(string name, CancellationToken cancellationToken = default(CancellationToken)) + { + Requires.NotNullOrEmpty(name, nameof(name)); + + await AwaitExtensions.SwitchOffMainThreadAsync(cancellationToken); + + var path = System.IO.Path.Combine(Path, name); + if (!Directory.Exists(path)) + throw new DirectoryNotFoundException($"Directory does not exist: {path}"); + return new NETCOREFolderImplementation(path, true); + } + + /// + public IList GetFolders() + { + EnsureExists(); + return Directory.GetDirectories(Path).Select(d => new NETCOREFolderImplementation(d, true)).ToList().AsReadOnly(); + } + /// + public async Task> GetFoldersAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + await AwaitExtensions.SwitchOffMainThreadAsync(cancellationToken); + + EnsureExists(); + return Directory.GetDirectories(Path).Select(d => new NETCOREFolderImplementation(d, true)).ToList().AsReadOnly(); + } + + /// + public ExistenceCheckResult CheckExists(string name) + { + Requires.NotNullOrEmpty(name, "name"); + + var checkPath = System.IO.Path.Combine(Path, name); + if (File.Exists(checkPath)) + return ExistenceCheckResult.FileExists; + else if (Directory.Exists(checkPath)) + return ExistenceCheckResult.FolderExists; + else + return ExistenceCheckResult.NotFound; + } + /// + public async Task CheckExistsAsync(string name, CancellationToken cancellationToken = default(CancellationToken)) + { + Requires.NotNullOrEmpty(name, "name"); + + await AwaitExtensions.SwitchOffMainThreadAsync(cancellationToken); + + var checkPath = System.IO.Path.Combine(Path, name); + if (File.Exists(checkPath)) + return ExistenceCheckResult.FileExists; + else if (Directory.Exists(checkPath)) + return ExistenceCheckResult.FolderExists; + else + return ExistenceCheckResult.NotFound; + } + + /// + public void Delete() + { + if (!_canDelete) + throw new IOException("Cannot delete root storage folder."); + + EnsureExists(); + Directory.Delete(Path, true); + } + /// + public async Task DeleteAsync(CancellationToken cancellationToken) + { + if (!_canDelete) + throw new IOException("Cannot delete root storage folder."); + + await AwaitExtensions.SwitchOffMainThreadAsync(cancellationToken); + + EnsureExists(); + Directory.Delete(Path, true); + } + + /// + public IFolder Move(IFolder folder, NameCollisionOption option = NameCollisionOption.ReplaceExisting) + { + Requires.NotNull(folder, nameof(folder)); + + EnsureExists(); + + var files = GetFiles(); + foreach (var file in files) + file.Move(System.IO.Path.Combine(folder.Path, file.Name), option); + + var folders = GetFolders(); + foreach (var nFolder in folders) + nFolder.Move(folder.CreateFolder(nFolder.Name, CreationCollisionOption.OpenIfExists), option); + + Delete(); + return folder; + } + /// + public async Task MoveAsync(IFolder folder, NameCollisionOption option = NameCollisionOption.ReplaceExisting, CancellationToken cancellationToken = default(CancellationToken)) + { + Requires.NotNull(folder, nameof(folder)); + + await AwaitExtensions.SwitchOffMainThreadAsync(cancellationToken); + + EnsureExists(); + + var files = await GetFilesAsync(cancellationToken: cancellationToken); + foreach (var file in files) + await file.MoveAsync(System.IO.Path.Combine(folder.Path, file.Name), option, cancellationToken); + + var folders = await GetFoldersAsync(cancellationToken); + foreach (var nFolder in folders) + await nFolder.MoveAsync(await folder.CreateFolderAsync(nFolder.Name, CreationCollisionOption.OpenIfExists, cancellationToken), option, cancellationToken); + + await DeleteAsync(cancellationToken); + return folder; + } + + void EnsureExists() + { + if (!Directory.Exists(Path)) + throw new DirectoryNotFoundException("Directory does not exist: " + Path); + } + } +} diff --git a/src/PCLExt.FileStorage.Core/PCLExt.FileStorage.Core.csproj b/src/PCLExt.FileStorage.Core/PCLExt.FileStorage.Core.csproj new file mode 100644 index 0000000..9733577 --- /dev/null +++ b/src/PCLExt.FileStorage.Core/PCLExt.FileStorage.Core.csproj @@ -0,0 +1,51 @@ + + + + netstandard1.3 + PCLExt.FileStorage + False + false + PCLExt.FileStorage + + + + TRACE;CORE + bin\Release\netstandard1.3\PCLExt.FileStorage.xml + + + + TRACE;DEBUG;CORE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/PCLExt.FileStorage.Core/Properties/AssemblyInfo.cs b/src/PCLExt.FileStorage.Core/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..ae5785a --- /dev/null +++ b/src/PCLExt.FileStorage.Core/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Reflection; + +[assembly: AssemblyTitle("PCLExt.FileStorage.Core")] +[assembly: AssemblyDescription("")] \ No newline at end of file diff --git a/src/PCLExt.FileStorage.Desktop/DesktopFileSystem.cs b/src/PCLExt.FileStorage.Desktop/DesktopFileSystem.cs deleted file mode 100644 index c4e11e6..0000000 --- a/src/PCLExt.FileStorage.Desktop/DesktopFileSystem.cs +++ /dev/null @@ -1,103 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Daniel Plaisted. All rights reserved. -// -// This file is a derivation of: -// https://github.com/dsplaisted/PCLStorage -// Which is released under the MS-PL license. -//----------------------------------------------------------------------- - -namespace PCLExt.FileStorage -{ - /// - /// Implementation of over classic .NET file I/O APIs. - /// - public class DesktopFileSystem : IFileSystem - { -#if MAC - [System.Runtime.InteropServices.DllImport(ObjCRuntime.Constants.FoundationLibrary)] - static extern System.IntPtr NSHomeDirectory(); // Under the sandbox, need to read the HomeDirectory - - static string MacHomeDirectory => ((Foundation.NSString)ObjCRuntime.Runtime.GetNSObject(NSHomeDirectory())).ToString(); -#endif - - /// - public IFolder BaseStorage - { - get - { -#if ANDROID || __IOS__ - var storage = ""; - return null; -#elif DESKTOP || MAC - var storage = System.AppDomain.CurrentDomain.BaseDirectory; -#endif - return new FileSystemFolder(storage); - } - } - - /// - public IFolder LocalStorage - { - get - { -#if ANDROID - var storage = Android.App.Application.Context.GetExternalFilesDir(null)?.ParentFile?.AbsolutePath; - if(string.IsNullOrEmpty(storage)) - return null; -#elif __IOS__ - var documents = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments); - var storage = System.IO.Path.Combine(documents, "..", "Library"); -#elif MAC - // (non-sandboxed) /Users/foo/Library/Application Support/ProcessName/ - // (sandboxed) /Users/foo/Library/Containers//Data/Library/Application Support/ProcessName/ - var name = System.IO.Path.GetFileNameWithoutExtension(System.Diagnostics.Process.GetCurrentProcess().ProcessName); - var storage = System.IO.Path.Combine(MacHomeDirectory, "Library", "Application Support"); - if(!string.IsNullOrEmpty(name)) - { - storage = System.IO.Path.Combine(storage, name); - if(!System.IO.Directory.Exists(storage)) // Ensure it exists to stope FileSystemFolder from throwing exception. - System.IO.Directory.CreateDirectory(storage); - } -#elif DESKTOP - var storage = System.Windows.Forms.Application.LocalUserAppDataPath; // SpecialFolder.LocalApplicationData is not app-specific, so use the Windows Forms API to get the app data path. -#endif - return new FileSystemFolder(storage); - } - } - - /// - public IFolder RoamingStorage - { - get - { -#if ANDROID - var storage = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments); - -#elif __IOS__ || MAC - var storage = ""; - return null; -#elif DESKTOP - var storage = System.Windows.Forms.Application.UserAppDataPath; // SpecialFolder.ApplicationData is not app-specific, so use the Windows Forms API to get the app data path. -#endif - return new FileSystemFolder(storage); - } - } - - /// - public IFolder SpecialStorage - { - get - { -#if DESKTOP || MAC - return BaseStorage; - -#elif ANDROID || __IOS__ - return LocalStorage; -#endif - - return null; - } - } - } -} diff --git a/src/PCLExt.FileStorage.Desktop/FileSystemFile.cs b/src/PCLExt.FileStorage.Desktop/NET4FileImplementation.cs similarity index 98% rename from src/PCLExt.FileStorage.Desktop/FileSystemFile.cs rename to src/PCLExt.FileStorage.Desktop/NET4FileImplementation.cs index 226ff50..0dd9b01 100644 --- a/src/PCLExt.FileStorage.Desktop/FileSystemFile.cs +++ b/src/PCLExt.FileStorage.Desktop/NET4FileImplementation.cs @@ -12,6 +12,7 @@ using System.IO; using System.Threading; using System.Threading.Tasks; +using PCLExt.FileStorage.Extensions; namespace PCLExt.FileStorage { @@ -19,7 +20,7 @@ namespace PCLExt.FileStorage /// Represents a file in the . /// [DebuggerDisplay("Name = {" + nameof(Name) + "}")] - public class FileSystemFile : IFile + internal class NET4FileImplementation : IFile { /// public string Name { get; private set; } @@ -30,7 +31,7 @@ public class FileSystemFile : IFile /// Creates a new corresponding to the specified path. /// /// The file path - public FileSystemFile(string path) + public NET4FileImplementation(string path) { Name = System.IO.Path.GetFileName(path); Path = path; diff --git a/src/PCLExt.FileStorage.Desktop/FileSystemFolder.cs b/src/PCLExt.FileStorage.Desktop/NET4FolderImplementation.cs similarity index 93% rename from src/PCLExt.FileStorage.Desktop/FileSystemFolder.cs rename to src/PCLExt.FileStorage.Desktop/NET4FolderImplementation.cs index ae57628..d9da568 100644 --- a/src/PCLExt.FileStorage.Desktop/FileSystemFolder.cs +++ b/src/PCLExt.FileStorage.Desktop/NET4FolderImplementation.cs @@ -14,6 +14,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using PCLExt.FileStorage.Extensions; namespace PCLExt.FileStorage { @@ -21,7 +22,7 @@ namespace PCLExt.FileStorage /// Represents a folder in the . /// [DebuggerDisplay("Name = {" + nameof(Name) + "}")] - public class FileSystemFolder : IFolder + internal class NET4FolderImplementation : IFolder { private readonly bool _canDelete; @@ -35,7 +36,7 @@ public class FileSystemFolder : IFolder /// /// The folder path. /// Specifies whether the folder can be deleted (via ). - public FileSystemFolder(string path, bool canDelete = false) { Name = System.IO.Path.GetFileName(path); Path = path; _canDelete = canDelete; } + public NET4FolderImplementation(string path, bool canDelete = false) { Name = System.IO.Path.GetFileName(path); Path = path; _canDelete = canDelete; } /// public IFile CreateFile(string desiredName, CreationCollisionOption option) @@ -79,7 +80,7 @@ public IFile CreateFile(string desiredName, CreationCollisionOption option) InternalCreateFile(newPath); } - return new FileSystemFile(newPath); + return new NET4FileImplementation(newPath); } /// public async Task CreateFileAsync(string desiredName, CreationCollisionOption option, CancellationToken cancellationToken = default(CancellationToken)) @@ -125,7 +126,7 @@ public IFile CreateFile(string desiredName, CreationCollisionOption option) InternalCreateFile(newPath); } - return new FileSystemFile(newPath); + return new NET4FileImplementation(newPath); } void InternalCreateFile(string path) { @@ -138,7 +139,7 @@ public IFile GetFile(string name) var path = System.IO.Path.Combine(Path, name); if (!File.Exists(path)) throw new FileNotFoundException($"File does not exist: {path}"); - return new FileSystemFile(path); + return new NET4FileImplementation(path); } /// public async Task GetFileAsync(string name, CancellationToken cancellationToken = default(CancellationToken)) @@ -148,14 +149,14 @@ public IFile GetFile(string name) var path = System.IO.Path.Combine(Path, name); if (!File.Exists(path)) throw new FileNotFoundException($"File does not exist: {path}"); - return new FileSystemFile(path); + return new NET4FileImplementation(path); } /// public IList GetFiles(string searchPattern = "*", FolderSearchOption searchOption = FolderSearchOption.TopFolderOnly) { EnsureExists(); - return Directory.GetFiles(Path, searchPattern, (SearchOption) searchOption).Select(f => new FileSystemFile(f)).ToList().AsReadOnly(); + return Directory.GetFiles(Path, searchPattern, (SearchOption) searchOption).Select(f => new NET4FileImplementation(f)).ToList().AsReadOnly(); } /// public async Task> GetFilesAsync(string searchPattern = "*", FolderSearchOption searchOption = FolderSearchOption.TopFolderOnly, CancellationToken cancellationToken = default(CancellationToken)) @@ -163,7 +164,7 @@ public IList GetFiles(string searchPattern = "*", FolderSearchOption sear await AwaitExtensions.SwitchOffMainThreadAsync(cancellationToken); EnsureExists(); - return Directory.GetFiles(Path, searchPattern, (SearchOption) searchOption).Select(f => new FileSystemFile(f)).ToList().AsReadOnly(); + return Directory.GetFiles(Path, searchPattern, (SearchOption) searchOption).Select(f => new NET4FileImplementation(f)).ToList().AsReadOnly(); } /// @@ -203,7 +204,7 @@ public IFolder CreateFolder(string desiredName, CreationCollisionOption option) else Directory.CreateDirectory(newPath); - return new FileSystemFolder(newPath, true); + return new NET4FolderImplementation(newPath, true); } /// public async Task CreateFolderAsync(string desiredName, CreationCollisionOption option, CancellationToken cancellationToken = default(CancellationToken)) @@ -245,7 +246,7 @@ public IFolder CreateFolder(string desiredName, CreationCollisionOption option) else Directory.CreateDirectory(newPath); - return new FileSystemFolder(newPath, true); + return new NET4FolderImplementation(newPath, true); } /// @@ -256,7 +257,7 @@ public IFolder GetFolder(string name) var path = System.IO.Path.Combine(Path, name); if (!Directory.Exists(path)) throw new DirectoryNotFoundException($"Directory does not exist: {path}"); - return new FileSystemFolder(path, true); + return new NET4FolderImplementation(path, true); } /// public async Task GetFolderAsync(string name, CancellationToken cancellationToken = default(CancellationToken)) @@ -268,14 +269,14 @@ public IFolder GetFolder(string name) var path = System.IO.Path.Combine(Path, name); if (!Directory.Exists(path)) throw new DirectoryNotFoundException($"Directory does not exist: {path}"); - return new FileSystemFolder(path, true); + return new NET4FolderImplementation(path, true); } /// public IList GetFolders() { EnsureExists(); - return Directory.GetDirectories(Path).Select(d => new FileSystemFolder(d, true)).ToList().AsReadOnly(); + return Directory.GetDirectories(Path).Select(d => new NET4FolderImplementation(d, true)).ToList().AsReadOnly(); } /// public async Task> GetFoldersAsync(CancellationToken cancellationToken = default(CancellationToken)) @@ -283,7 +284,7 @@ public IList GetFolders() await AwaitExtensions.SwitchOffMainThreadAsync(cancellationToken); EnsureExists(); - return Directory.GetDirectories(Path).Select(d => new FileSystemFolder(d, true)).ToList().AsReadOnly(); + return Directory.GetDirectories(Path).Select(d => new NET4FolderImplementation(d, true)).ToList().AsReadOnly(); } /// diff --git a/src/PCLExt.FileStorage.Desktop/PCLExt.FileStorage.Desktop.csproj b/src/PCLExt.FileStorage.Desktop/PCLExt.FileStorage.Desktop.csproj index 16d7674..ee5bdd9 100644 --- a/src/PCLExt.FileStorage.Desktop/PCLExt.FileStorage.Desktop.csproj +++ b/src/PCLExt.FileStorage.Desktop/PCLExt.FileStorage.Desktop.csproj @@ -34,42 +34,80 @@ - - - - - - - + Properties\CommonAssemblyInfo.cs - - AwaitExtensions.cs + + BaseFile.cs - - FileSystem.cs + + BaseFolder.cs - - PortablePath.cs + + CreationCollisionOption.cs - - Requires.cs + + ExistenceCheckResult.cs - - BaseFolder.cs + + FolderSearchOption.cs - - BaseFile.cs + + IFile.cs + + + IFolder.cs + + + NameCollisionOption.cs + + + Exceptions\DirectoryNotFoundException.cs + + + Exceptions\ExceptionsHelper.cs + + + Exceptions\FileNotFoundException.cs + + + Extensions\AwaitExtensions.cs + + + Extensions\FileExtensions.cs + + + Extensions\FolderExtensions.cs + + + Files\FileFromPath.cs + + + Folders\ApplicationFolder.cs + + + Folders\FolderFromPath.cs + + + Folders\LocalFolder.cs + + + Folders\RoamingFolder.cs + + + PortablePath.cs + + + Requires.cs - - - + + - + {3F8D0494-6EE2-47FE-B263-F09A5EA3D0B3} PCLExt.FileStorage.Abstractions diff --git a/src/PCLExt.FileStorage.Mac/PCLExt.FileStorage.Mac.csproj b/src/PCLExt.FileStorage.Mac/PCLExt.FileStorage.Mac.csproj index 6645ef6..b718c4d 100644 --- a/src/PCLExt.FileStorage.Mac/PCLExt.FileStorage.Mac.csproj +++ b/src/PCLExt.FileStorage.Mac/PCLExt.FileStorage.Mac.csproj @@ -51,34 +51,76 @@ - - AwaitExtensions.cs + + BaseFile.cs - - FileSystem.cs + + BaseFolder.cs - - PortablePath.cs + + CreationCollisionOption.cs - - Requires.cs + + ExistenceCheckResult.cs - - BaseFolder.cs + + FolderSearchOption.cs - - BaseFile.cs + + IFile.cs + + + IFolder.cs + + + NameCollisionOption.cs + + + Exceptions\DirectoryNotFoundException.cs + + + Exceptions\ExceptionsHelper.cs + + + Exceptions\FileNotFoundException.cs - - DesktopFileSystem.cs + + Extensions\AwaitExtensions.cs - - FileSystemFolder.cs + + Extensions\FileExtensions.cs - - FileSystemFile.cs + + Extensions\FolderExtensions.cs + + + Files\FileFromPath.cs + + + Folders\ApplicationFolder.cs + + + Folders\FolderFromPath.cs + + + Folders\LocalFolder.cs + + + Folders\RoamingFolder.cs + + + NET4FileImplementation.cs + + + NET4FolderImplementation.cs + + + PortablePath.cs + + + Requires.cs - + Properties\CommonAssemblyInfo.cs @@ -86,7 +128,7 @@ - + {3F8D0494-6EE2-47FE-B263-F09A5EA3D0B3} PCLExt.FileStorage.Abstractions diff --git a/src/PCLExt.FileStorage/BaseFile.cs b/src/PCLExt.FileStorage.Portable.Abstractions/BaseFile.cs similarity index 93% rename from src/PCLExt.FileStorage/BaseFile.cs rename to src/PCLExt.FileStorage.Portable.Abstractions/BaseFile.cs index 783e0a1..3384a8e 100644 --- a/src/PCLExt.FileStorage/BaseFile.cs +++ b/src/PCLExt.FileStorage.Portable.Abstractions/BaseFile.cs @@ -21,11 +21,6 @@ public abstract class BaseFile : IFile /// /// public BaseFile(IFile file) { _file = file; } - /// - /// - /// - /// - public BaseFile(string path) : this(FileSystem.GetFileFromPath(path)) { } /// public Stream Open(FileAccess fileAccess) => _file.Open(fileAccess); @@ -52,4 +47,4 @@ public BaseFile(string path) : this(FileSystem.GetFileFromPath(path)) { } /// public Task CopyAsync(string newPath, NameCollisionOption collisionOption = NameCollisionOption.ReplaceExisting, CancellationToken cancellationToken = default(CancellationToken)) => _file.CopyAsync(newPath, collisionOption, cancellationToken); } -} +} \ No newline at end of file diff --git a/src/PCLExt.FileStorage/BaseFolder.cs b/src/PCLExt.FileStorage.Portable.Abstractions/BaseFolder.cs similarity index 95% rename from src/PCLExt.FileStorage/BaseFolder.cs rename to src/PCLExt.FileStorage.Portable.Abstractions/BaseFolder.cs index 36b7c78..a3f16b6 100644 --- a/src/PCLExt.FileStorage/BaseFolder.cs +++ b/src/PCLExt.FileStorage.Portable.Abstractions/BaseFolder.cs @@ -21,11 +21,6 @@ public abstract class BaseFolder : IFolder /// /// protected BaseFolder(IFolder folder) { _folder = folder; } - /// - /// - /// - /// - protected BaseFolder(string path) : this(FileSystem.GetFolderFromPath(path)) { } /// public IFile GetFile(string name) => _folder.GetFile(name); @@ -72,4 +67,4 @@ protected BaseFolder(string path) : this(FileSystem.GetFolderFromPath(path)) { } /// public Task MoveAsync(IFolder folder, NameCollisionOption option = NameCollisionOption.ReplaceExisting, CancellationToken cancellationToken = new CancellationToken()) => _folder.MoveAsync(folder, option, cancellationToken); } -} +} \ No newline at end of file diff --git a/src/PCLExt.FileStorage.Portable.Abstractions/CreationCollisionOption.cs b/src/PCLExt.FileStorage.Portable.Abstractions/CreationCollisionOption.cs new file mode 100644 index 0000000..5a57f99 --- /dev/null +++ b/src/PCLExt.FileStorage.Portable.Abstractions/CreationCollisionOption.cs @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Daniel Plaisted. All rights reserved. +// +// This file is a derivation of: +// https://github.com/dsplaisted/PCLStorage +// Which is released under the MS-PL license. +//----------------------------------------------------------------------- + +namespace PCLExt.FileStorage +{ + /// + /// Specifies what should happen when trying to create a file or folder that already exists. + /// + public enum CreationCollisionOption + { + /// + /// Creates a new file with a unique name of the form "name (2).txt" + /// + GenerateUniqueName = 0, + /// + /// Replaces any existing file with a new (empty) one + /// + ReplaceExisting = 1, + /// + /// Throws an exception if the file exists + /// + FailIfExists = 2, + /// + /// Opens the existing file, if any + /// + OpenIfExists = 3, + } +} \ No newline at end of file diff --git a/src/PCLExt.FileStorage.Abstractions/ExistenceCheckResult.cs b/src/PCLExt.FileStorage.Portable.Abstractions/ExistenceCheckResult.cs similarity index 99% rename from src/PCLExt.FileStorage.Abstractions/ExistenceCheckResult.cs rename to src/PCLExt.FileStorage.Portable.Abstractions/ExistenceCheckResult.cs index 61c3430..9dbca4e 100644 --- a/src/PCLExt.FileStorage.Abstractions/ExistenceCheckResult.cs +++ b/src/PCLExt.FileStorage.Portable.Abstractions/ExistenceCheckResult.cs @@ -29,4 +29,4 @@ public enum ExistenceCheckResult /// FolderExists, } -} +} \ No newline at end of file diff --git a/src/PCLExt.FileStorage.Abstractions/FolderSearchOption.cs b/src/PCLExt.FileStorage.Portable.Abstractions/FolderSearchOption.cs similarity index 98% rename from src/PCLExt.FileStorage.Abstractions/FolderSearchOption.cs rename to src/PCLExt.FileStorage.Portable.Abstractions/FolderSearchOption.cs index 6c40b05..485fce8 100644 --- a/src/PCLExt.FileStorage.Abstractions/FolderSearchOption.cs +++ b/src/PCLExt.FileStorage.Portable.Abstractions/FolderSearchOption.cs @@ -5,4 +5,4 @@ public enum FolderSearchOption TopFolderOnly, AllFolders, } -} +} \ No newline at end of file diff --git a/src/PCLExt.FileStorage.Abstractions/IFile.cs b/src/PCLExt.FileStorage.Portable.Abstractions/IFile.cs similarity index 100% rename from src/PCLExt.FileStorage.Abstractions/IFile.cs rename to src/PCLExt.FileStorage.Portable.Abstractions/IFile.cs diff --git a/src/PCLExt.FileStorage.Abstractions/IFolder.cs b/src/PCLExt.FileStorage.Portable.Abstractions/IFolder.cs similarity index 92% rename from src/PCLExt.FileStorage.Abstractions/IFolder.cs rename to src/PCLExt.FileStorage.Portable.Abstractions/IFolder.cs index 531e74e..272ccca 100644 --- a/src/PCLExt.FileStorage.Abstractions/IFolder.cs +++ b/src/PCLExt.FileStorage.Portable.Abstractions/IFolder.cs @@ -13,29 +13,6 @@ namespace PCLExt.FileStorage { - /// - /// Specifies what should happen when trying to create a file or folder that already exists. - /// - public enum CreationCollisionOption - { - /// - /// Creates a new file with a unique name of the form "name (2).txt" - /// - GenerateUniqueName = 0, - /// - /// Replaces any existing file with a new (empty) one - /// - ReplaceExisting = 1, - /// - /// Throws an exception if the file exists - /// - FailIfExists = 2, - /// - /// Opens the existing file, if any - /// - OpenIfExists = 3, - } - /// /// Represents a file system folder /// diff --git a/src/PCLExt.FileStorage.Abstractions/NameCollisionOption.cs b/src/PCLExt.FileStorage.Portable.Abstractions/NameCollisionOption.cs similarity index 100% rename from src/PCLExt.FileStorage.Abstractions/NameCollisionOption.cs rename to src/PCLExt.FileStorage.Portable.Abstractions/NameCollisionOption.cs diff --git a/src/PCLExt.FileStorage.Abstractions/PCLExt.FileStorage.Abstractions.csproj b/src/PCLExt.FileStorage.Portable.Abstractions/PCLExt.FileStorage.Portable.Abstractions.csproj similarity index 92% rename from src/PCLExt.FileStorage.Abstractions/PCLExt.FileStorage.Abstractions.csproj rename to src/PCLExt.FileStorage.Portable.Abstractions/PCLExt.FileStorage.Portable.Abstractions.csproj index 89a7188..a4f5799 100644 --- a/src/PCLExt.FileStorage.Abstractions/PCLExt.FileStorage.Abstractions.csproj +++ b/src/PCLExt.FileStorage.Portable.Abstractions/PCLExt.FileStorage.Portable.Abstractions.csproj @@ -34,14 +34,15 @@ bin\Release\PCLExt.FileStorage.Abstractions.xml - + Properties\CommonAssemblyInfo.cs + + + - - diff --git a/src/PCLExt.FileStorage.Abstractions/Properties/AssemblyInfo.cs b/src/PCLExt.FileStorage.Portable.Abstractions/Properties/AssemblyInfo.cs similarity index 100% rename from src/PCLExt.FileStorage.Abstractions/Properties/AssemblyInfo.cs rename to src/PCLExt.FileStorage.Portable.Abstractions/Properties/AssemblyInfo.cs diff --git a/src/PCLExt.FileStorage/Exceptions.cs b/src/PCLExt.FileStorage.Portable/Exceptions/DirectoryNotFoundException.cs similarity index 57% rename from src/PCLExt.FileStorage/Exceptions.cs rename to src/PCLExt.FileStorage.Portable/Exceptions/DirectoryNotFoundException.cs index 9b4b44f..70ddc60 100644 --- a/src/PCLExt.FileStorage/Exceptions.cs +++ b/src/PCLExt.FileStorage.Portable/Exceptions/DirectoryNotFoundException.cs @@ -1,4 +1,4 @@ -//----------------------------------------------------------------------- +//----------------------------------------------------------------------- // // Copyright (c) Daniel Plaisted. All rights reserved. // @@ -8,30 +8,14 @@ //----------------------------------------------------------------------- using System; -using System.IO; -namespace PCLExt.FileStorage +namespace PCLExt.FileStorage.Exceptions { - /// - public class FileNotFoundException -#if PORTABLE - : IOException -#else - : System.IO.FileNotFoundException -#endif - { - /// - public FileNotFoundException(string message) : base(message) { } - - /// - public FileNotFoundException(string message, Exception innerException) : base(message, innerException) { } - } - /// public class DirectoryNotFoundException #if PORTABLE - : IOException -#elif NETFX_CORE + : System.IO.IOException +#elif CORE : System.IO.FileNotFoundException #else : System.IO.DirectoryNotFoundException @@ -43,4 +27,4 @@ public DirectoryNotFoundException(string message) : base(message) { } /// public DirectoryNotFoundException(string message, Exception innerException) : base(message, innerException) { } } -} +} \ No newline at end of file diff --git a/src/PCLExt.FileStorage.Portable/Exceptions/ExceptionsHelper.cs b/src/PCLExt.FileStorage.Portable/Exceptions/ExceptionsHelper.cs new file mode 100644 index 0000000..a0bb0dd --- /dev/null +++ b/src/PCLExt.FileStorage.Portable/Exceptions/ExceptionsHelper.cs @@ -0,0 +1,15 @@ +using System; + +namespace PCLExt.FileStorage.Exceptions +{ + internal static class ExceptionsHelper + { + internal static Exception NotImplementedInReferenceAssembly() => + new NotImplementedException(@"This functionality is not implemented in the portable version of this assembly. +You should reference the PCLExt.FileStorage NuGet package from your main application project in order to reference the platform-specific implementation."); + + internal static Exception NotImplementedInNetStandard() => + new NotImplementedException(@"This functionality is not implemented in the current version of this .NET Standard. +You should wait for .NET Standard 2.0."); + } +} diff --git a/src/PCLExt.FileStorage.Portable/Exceptions/FileNotFoundException.cs b/src/PCLExt.FileStorage.Portable/Exceptions/FileNotFoundException.cs new file mode 100644 index 0000000..e6e6293 --- /dev/null +++ b/src/PCLExt.FileStorage.Portable/Exceptions/FileNotFoundException.cs @@ -0,0 +1,28 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Daniel Plaisted. All rights reserved. +// +// This file is a derivation of: +// https://github.com/dsplaisted/PCLStorage +// Which is released under the MS-PL license. +//----------------------------------------------------------------------- + +using System; + +namespace PCLExt.FileStorage.Exceptions +{ + /// + public class FileNotFoundException +#if PORTABLE + : System.IO.IOException +#else + : System.IO.FileNotFoundException +#endif + { + /// + public FileNotFoundException(string message) : base(message) { } + + /// + public FileNotFoundException(string message, Exception innerException) : base(message, innerException) { } + } +} \ No newline at end of file diff --git a/src/PCLExt.FileStorage/AwaitExtensions.cs b/src/PCLExt.FileStorage.Portable/Extensions/AwaitExtensions.cs similarity index 98% rename from src/PCLExt.FileStorage/AwaitExtensions.cs rename to src/PCLExt.FileStorage.Portable/Extensions/AwaitExtensions.cs index 66ccfb6..e369d64 100644 --- a/src/PCLExt.FileStorage/AwaitExtensions.cs +++ b/src/PCLExt.FileStorage.Portable/Extensions/AwaitExtensions.cs @@ -12,7 +12,7 @@ using System.Threading; using System.Threading.Tasks; -namespace PCLExt.FileStorage +namespace PCLExt.FileStorage.Extensions { /// /// Extensions for use internally by PCLStorage for awaiting. diff --git a/src/PCLExt.FileStorage.Abstractions/FileExtensions.cs b/src/PCLExt.FileStorage.Portable/Extensions/FileExtensions.cs similarity index 99% rename from src/PCLExt.FileStorage.Abstractions/FileExtensions.cs rename to src/PCLExt.FileStorage.Portable/Extensions/FileExtensions.cs index a5ba658..ed36e57 100644 --- a/src/PCLExt.FileStorage.Abstractions/FileExtensions.cs +++ b/src/PCLExt.FileStorage.Portable/Extensions/FileExtensions.cs @@ -11,7 +11,7 @@ using System.IO; using System.Threading.Tasks; -namespace PCLExt.FileStorage +namespace PCLExt.FileStorage.Extensions { /// /// Provides extension methods for the class @@ -206,4 +206,4 @@ public static async Task AppendLinesAsync(this IFile file, IEnumerable l } } } -} +} \ No newline at end of file diff --git a/src/PCLExt.FileStorage.Portable/Extensions/FolderExtensions.cs b/src/PCLExt.FileStorage.Portable/Extensions/FolderExtensions.cs new file mode 100644 index 0000000..e208a48 --- /dev/null +++ b/src/PCLExt.FileStorage.Portable/Extensions/FolderExtensions.cs @@ -0,0 +1,85 @@ +using System; +using System.Linq; + +namespace PCLExt.FileStorage.Extensions +{ + /// + /// Provides extension methods for and class + /// + public static class FolderExtensions + { + /// + /// + /// + /// + /// + /// + public static IFolder GetFolderFromPath(this IFolder folder, string folderPath) + { + var folders = folderPath.Split(new[] + { +#if DESKTOP || ANDROID || __IOS__ || MAC || PORTABLE + PortablePath.DirectorySeparatorChar +#elif CORE + System.IO.Path.DirectorySeparatorChar +#endif + }, StringSplitOptions.RemoveEmptyEntries); + + return GetFolderFromPath(folder, folders); + } + + /// + /// + /// + /// + /// + /// + public static IFolder GetFolderFromPath(this IFolder folder, params string[] folders) + { + if (folders != null) + foreach (var folderName in folders) + folder = folder.CreateFolder(folderName, CreationCollisionOption.OpenIfExists); + + return folder; + } + + + /// + /// + /// + /// + /// + /// + public static IFile GetFileFromPath(this IFolder folder, string filePath) + { + var foldersWithFile = filePath.Split(new[] + { +#if DESKTOP || ANDROID || __IOS__ || MAC || PORTABLE + PortablePath.DirectorySeparatorChar +#elif CORE + System.IO.Path.DirectorySeparatorChar +#endif + }, StringSplitOptions.RemoveEmptyEntries); + + return GetFileFromPath(folder, foldersWithFile); + } + + /// + /// + /// + /// + /// + /// + public static IFile GetFileFromPath(this IFolder folder, params string[] foldersWithFile) + { + var fileName = foldersWithFile.Last(); + var folders = foldersWithFile.Length > 1 ? foldersWithFile.Take(foldersWithFile.Length - 1).ToArray() : null; + + if (folders != null) + foreach (var folderName in folders) + folder = folder.CreateFolder(folderName, CreationCollisionOption.OpenIfExists); + + return folder.CreateFile(fileName, CreationCollisionOption.OpenIfExists); + } + } +} diff --git a/src/PCLExt.FileStorage.Portable/Files/FileFromPath.cs b/src/PCLExt.FileStorage.Portable/Files/FileFromPath.cs new file mode 100644 index 0000000..e2de2b5 --- /dev/null +++ b/src/PCLExt.FileStorage.Portable/Files/FileFromPath.cs @@ -0,0 +1,23 @@ +namespace PCLExt.FileStorage.Files +{ + public class FileFromPath : BaseFile + { + /// + /// + /// + /// + public FileFromPath(string path) : base(GetFileFromPath(path)) { } + private static IFile GetFileFromPath(string path) + { + Requires.NotNullOrEmpty(path, "path"); + +#if DESKTOP || ANDROID || __IOS__ || MAC + return System.IO.File.Exists(path) ? new NET4FileImplementation(path) : null; +#elif CORE + return System.IO.File.Exists(path) ? new NETCOREFileImplementation(path) : null; +#endif + + throw Exceptions.ExceptionsHelper.NotImplementedInReferenceAssembly(); + } + } +} diff --git a/src/PCLExt.FileStorage.Portable/Folders/ApplicationFolder.cs b/src/PCLExt.FileStorage.Portable/Folders/ApplicationFolder.cs new file mode 100644 index 0000000..070db09 --- /dev/null +++ b/src/PCLExt.FileStorage.Portable/Folders/ApplicationFolder.cs @@ -0,0 +1,29 @@ +namespace PCLExt.FileStorage.Folders +{ + /// + /// A folder representing storage which is where the app is running. + /// + public class ApplicationFolder : BaseFolder + { + /// + /// + /// +#if DESKTOP || ANDROID || __IOS__ || MAC + public ApplicationFolder() : base(GetApplicationFolder()) { } + private static IFolder GetApplicationFolder() + { +#if ANDROID || __IOS__ + var storage = ""; + return null; +#elif DESKTOP || MAC + var storage = System.AppDomain.CurrentDomain.BaseDirectory; +#endif + return new NET4FolderImplementation(storage); + } +#elif CORE + public ApplicationFolder() : base(new NETCOREFolderImplementation(System.AppContext.BaseDirectory)) { } +#else + public ApplicationFolder() : base(null) => throw Exceptions.ExceptionsHelper.NotImplementedInReferenceAssembly(); +#endif + } +} \ No newline at end of file diff --git a/src/PCLExt.FileStorage.Portable/Folders/FolderFromPath.cs b/src/PCLExt.FileStorage.Portable/Folders/FolderFromPath.cs new file mode 100644 index 0000000..e8e95b8 --- /dev/null +++ b/src/PCLExt.FileStorage.Portable/Folders/FolderFromPath.cs @@ -0,0 +1,24 @@ +namespace PCLExt.FileStorage.Folders +{ + public class FolderFromPath : BaseFolder + { + /// + /// + /// + /// + public FolderFromPath(string path) : base(GetFolderFromPath(path)) { } + private static IFolder GetFolderFromPath(string path) + { + Requires.NotNullOrEmpty(path, "path"); + +#if DESKTOP || ANDROID || __IOS__ || MAC + return System.IO.Directory.Exists(path) ? new NET4FolderImplementation(path, true) : null; + +#elif CORE + return System.IO.Directory.Exists(path) ? new NETCOREFolderImplementation(path, true) : null; +#endif + + throw Exceptions.ExceptionsHelper.NotImplementedInReferenceAssembly(); + } + } +} diff --git a/src/PCLExt.FileStorage.Portable/Folders/LocalFolder.cs b/src/PCLExt.FileStorage.Portable/Folders/LocalFolder.cs new file mode 100644 index 0000000..62a2f62 --- /dev/null +++ b/src/PCLExt.FileStorage.Portable/Folders/LocalFolder.cs @@ -0,0 +1,51 @@ +namespace PCLExt.FileStorage.Folders +{ + /// + /// A folder representing storage which is local to the current device. + /// + public class LocalFolder : BaseFolder + { +#if MAC + [System.Runtime.InteropServices.DllImport(ObjCRuntime.Constants.FoundationLibrary)] + static extern System.IntPtr NSHomeDirectory(); // Under the sandbox, need to read the HomeDirectory + + static string MacHomeDirectory => ((Foundation.NSString)ObjCRuntime.Runtime.GetNSObject(NSHomeDirectory())).ToString(); +#endif + + /// + /// + /// +#if DESKTOP || ANDROID || __IOS__ || MAC + public LocalFolder() : base(GetLocalFolder()) { } + private static IFolder GetLocalFolder() + { +#if ANDROID + var storage = Android.App.Application.Context.GetExternalFilesDir(null)?.ParentFile?.AbsolutePath; + if(string.IsNullOrEmpty(storage)) + return null; +#elif __IOS__ + var documents = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments); + var storage = System.IO.Path.Combine(documents, "..", "Library"); +#elif MAC + // (non-sandboxed) /Users/foo/Library/Application Support/ProcessName/ + // (sandboxed) /Users/foo/Library/Containers//Data/Library/Application Support/ProcessName/ + var name = System.IO.Path.GetFileNameWithoutExtension(System.Diagnostics.Process.GetCurrentProcess().ProcessName); + var storage = System.IO.Path.Combine(MacHomeDirectory, "Library", "Application Support"); + if(!string.IsNullOrEmpty(name)) + { + storage = System.IO.Path.Combine(storage, name); + if(!System.IO.Directory.Exists(storage)) // Ensure it exists to stope FileSystemFolder from throwing exception. + System.IO.Directory.CreateDirectory(storage); + } +#elif DESKTOP + var storage = System.Windows.Forms.Application.LocalUserAppDataPath; // SpecialFolder.LocalApplicationData is not app-specific, so use the Windows Forms API to get the app data path. +#endif + return new NET4FolderImplementation(storage); + } +#elif CORE + public LocalFolder() : base(null) => throw Exceptions.ExceptionsHelper.NotImplementedInNetStandard(); +#else + public LocalFolder() : base(null) => throw Exceptions.ExceptionsHelper.NotImplementedInReferenceAssembly(); +#endif + } +} \ No newline at end of file diff --git a/src/PCLExt.FileStorage.Portable/Folders/RoamingFolder.cs b/src/PCLExt.FileStorage.Portable/Folders/RoamingFolder.cs new file mode 100644 index 0000000..cb9be96 --- /dev/null +++ b/src/PCLExt.FileStorage.Portable/Folders/RoamingFolder.cs @@ -0,0 +1,32 @@ +namespace PCLExt.FileStorage.Folders +{ + /// + /// A folder representing storage which may be synced with other devices for the same user. + /// + public class RoamingFolder : BaseFolder + { + /// + /// + /// +#if DESKTOP || ANDROID || __IOS__ || MAC + public RoamingFolder() : base(GetRoamingFolder()) { } + private static IFolder GetRoamingFolder() + { +#if ANDROID + var storage = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments); + +#elif __IOS__ || MAC + var storage = ""; + return null; +#elif DESKTOP + var storage = System.Windows.Forms.Application.UserAppDataPath; // SpecialFolder.ApplicationData is not app-specific, so use the Windows Forms API to get the app data path. +#endif + return new NET4FolderImplementation(storage); + } +#elif CORE + public RoamingFolder() : base(null) => throw Exceptions.ExceptionsHelper.NotImplementedInNetStandard(); +#else + public RoamingFolder() : base(null) => throw Exceptions.ExceptionsHelper.NotImplementedInReferenceAssembly(); +#endif + } +} \ No newline at end of file diff --git a/src/PCLExt.FileStorage/PCLExt.FileStorage.csproj b/src/PCLExt.FileStorage.Portable/PCLExt.FileStorage.Portable.csproj similarity index 76% rename from src/PCLExt.FileStorage/PCLExt.FileStorage.csproj rename to src/PCLExt.FileStorage.Portable/PCLExt.FileStorage.Portable.csproj index a86df7d..a3c2bf1 100644 --- a/src/PCLExt.FileStorage/PCLExt.FileStorage.csproj +++ b/src/PCLExt.FileStorage.Portable/PCLExt.FileStorage.Portable.csproj @@ -37,20 +37,26 @@ - + Properties\CommonAssemblyInfo.cs - - - - + + + + + + + + + + - + - + {3F8D0494-6EE2-47FE-B263-F09A5EA3D0B3} PCLExt.FileStorage.Abstractions diff --git a/src/PCLExt.FileStorage/PortablePath.cs b/src/PCLExt.FileStorage.Portable/PortablePath.cs similarity index 91% rename from src/PCLExt.FileStorage/PortablePath.cs rename to src/PCLExt.FileStorage.Portable/PortablePath.cs index 8667019..4e2951d 100644 --- a/src/PCLExt.FileStorage/PortablePath.cs +++ b/src/PCLExt.FileStorage.Portable/PortablePath.cs @@ -7,6 +7,8 @@ // Which is released under the MS-PL license. //----------------------------------------------------------------------- +using PCLExt.FileStorage.Exceptions; + namespace PCLExt.FileStorage { /// @@ -25,7 +27,7 @@ public static char DirectorySeparatorChar return System.IO.Path.DirectorySeparatorChar; #endif - throw FileSystem.NotImplementedInReferenceAssembly(); + throw ExceptionsHelper.NotImplementedInReferenceAssembly(); } } @@ -40,7 +42,7 @@ public static string Combine(params string[] paths) return System.IO.Path.Combine(paths); #endif - throw FileSystem.NotImplementedInReferenceAssembly(); + throw ExceptionsHelper.NotImplementedInReferenceAssembly(); } /// @@ -60,7 +62,7 @@ public static string GetExtension(string path) return System.IO.Path.GetExtension(path); #endif - throw FileSystem.NotImplementedInReferenceAssembly(); + throw ExceptionsHelper.NotImplementedInReferenceAssembly(); } /// @@ -84,7 +86,7 @@ public static string GetFileName(string path) return System.IO.Path.GetFileName(path); #endif - throw FileSystem.NotImplementedInReferenceAssembly(); + throw ExceptionsHelper.NotImplementedInReferenceAssembly(); } /// @@ -103,7 +105,7 @@ public static string GetFileNameWithoutExtension(string path) return System.IO.Path.GetFileNameWithoutExtension(path); #endif - throw FileSystem.NotImplementedInReferenceAssembly(); + throw ExceptionsHelper.NotImplementedInReferenceAssembly(); } /// @@ -123,7 +125,7 @@ public static bool HasExtension(string path) return System.IO.Path.HasExtension(path); #endif - throw FileSystem.NotImplementedInReferenceAssembly(); + throw ExceptionsHelper.NotImplementedInReferenceAssembly(); } } } diff --git a/src/PCLExt.FileStorage/Properties/AssemblyInfo.cs b/src/PCLExt.FileStorage.Portable/Properties/AssemblyInfo.cs similarity index 100% rename from src/PCLExt.FileStorage/Properties/AssemblyInfo.cs rename to src/PCLExt.FileStorage.Portable/Properties/AssemblyInfo.cs diff --git a/src/PCLExt.FileStorage/Requires.cs b/src/PCLExt.FileStorage.Portable/Requires.cs similarity index 100% rename from src/PCLExt.FileStorage/Requires.cs rename to src/PCLExt.FileStorage.Portable/Requires.cs diff --git a/src/PCLExt.FileStorage.iOS/PCLExt.FileStorage.iOS.csproj b/src/PCLExt.FileStorage.iOS/PCLExt.FileStorage.iOS.csproj index 3223069..88c2362 100644 --- a/src/PCLExt.FileStorage.iOS/PCLExt.FileStorage.iOS.csproj +++ b/src/PCLExt.FileStorage.iOS/PCLExt.FileStorage.iOS.csproj @@ -37,46 +37,87 @@ - - + Properties\CommonAssemblyInfo.cs - - DesktopFileSystem.cs + + NET4FileImplementation.cs - - FileSystemFile.cs + + NET4FolderImplementation.cs - - FileSystemFolder.cs + + BaseFile.cs - - AwaitExtensions.cs + + BaseFolder.cs - - PortablePath.cs + + CreationCollisionOption.cs - - Requires.cs + + ExistenceCheckResult.cs - - BaseFolder.cs + + FolderSearchOption.cs - - BaseFile.cs + + IFile.cs - - - FileSystem.cs + + IFolder.cs + + + NameCollisionOption.cs + + + Exceptions\DirectoryNotFoundException.cs + + + Exceptions\ExceptionsHelper.cs + + + Exceptions\FileNotFoundException.cs + + + Extensions\AwaitExtensions.cs + + Extensions\FileExtensions.cs + + + Extensions\FolderExtensions.cs + + + Files\FileFromPath.cs + + + Folders\ApplicationFolder.cs + + + Folders\FolderFromPath.cs + + + Folders\LocalFolder.cs + + + Folders\RoamingFolder.cs + + + PortablePath.cs + + + Requires.cs + + - + {3F8D0494-6EE2-47FE-B263-F09A5EA3D0B3} PCLExt.FileStorage.Abstractions diff --git a/src/PCLExt.FileStorage/FileSystem.cs b/src/PCLExt.FileStorage/FileSystem.cs deleted file mode 100644 index 0c95b49..0000000 --- a/src/PCLExt.FileStorage/FileSystem.cs +++ /dev/null @@ -1,153 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Daniel Plaisted. All rights reserved. -// -// This file is a derivation of: -// https://github.com/dsplaisted/PCLStorage -// Which is released under the MS-PL license. -//----------------------------------------------------------------------- - -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace PCLExt.FileStorage -{ - /// - /// Provides access to an implementation of for the current platform. - /// - public static class FileSystem - { - internal static Exception NotImplementedInReferenceAssembly() => - new NotImplementedException(@"This functionality is not implemented in the portable version of this assembly. -You should reference the PCLExt.FileStorage NuGet package from your main application project in order to reference the platform-specific implementation."); - - - /// - /// The implementation of for the current platform. - /// - private static IFileSystem Current - { - get - { -#if DESKTOP || ANDROID || __IOS__ || MAC - return _fileSystem ?? (_fileSystem = new DesktopFileSystem()); -#endif - - throw NotImplementedInReferenceAssembly(); - } - } - private static IFileSystem _fileSystem; - - -#if MAC - [System.Runtime.InteropServices.DllImport(ObjCRuntime.Constants.FoundationLibrary)] - static extern System.IntPtr NSHomeDirectory(); // Under the sandbox, need to read the HomeDirectory - - static string MacHomeDirectory => ((Foundation.NSString)ObjCRuntime.Runtime.GetNSObject(NSHomeDirectory())).ToString(); -#endif - - /// - /// A folder representing storage which is where the app is running. - /// - public static IFolder BaseStorage => Current.BaseStorage; - - /// - /// A folder representing storage which is local to the current device. - /// - public static IFolder LocalStorage => Current.LocalStorage; - - /// - /// A folder representing storage which may be synced with other devices for the same user. - /// - public static IFolder RoamingStorage => Current.RoamingStorage; - - /// - /// A folder representing storage which may be synced with other devices for the same user. - /// - public static IFolder SpecialStorage => Current.SpecialStorage; - - - - - /// - /// Gets a file, given its path. Returns null if the file does not exist. - /// - /// The path to a file, as returned from the property. - /// A file for the given path, or null if it does not exist. - public static IFile GetFileFromPath(string path) - { - Requires.NotNullOrEmpty(path, "path"); - -#if DESKTOP || ANDROID || __IOS__ || MAC - if (System.IO.File.Exists(path)) - return new FileSystemFile(path); - - return null; -#endif - - throw NotImplementedInReferenceAssembly(); - } - - /// - /// Gets a file, given its path. Returns null if the file does not exist. - /// - /// The path to a file, as returned from the property. - /// The cancellation token. - /// A file for the given path, or null if it does not exist. - public static async Task GetFileFromPathAsync(string path, CancellationToken cancellationToken) - { - Requires.NotNullOrEmpty(path, "path"); - -#if DESKTOP || ANDROID || __IOS__ || MAC - await AwaitExtensions.SwitchOffMainThreadAsync(cancellationToken); - if (System.IO.File.Exists(path)) - return new FileSystemFile(path); - - return null; -#endif - - throw NotImplementedInReferenceAssembly(); - } - - /// - /// Gets a folder, given its path. Returns null if the folder does not exist. - /// - /// The path to a folder, as returned from the property. - /// A folder for the specified path, or null if it does not exist. - public static IFolder GetFolderFromPath(string path) - { - Requires.NotNullOrEmpty(path, "path"); - -#if DESKTOP || ANDROID || __IOS__ || MAC - if (System.IO.Directory.Exists(path)) - return new FileSystemFolder(path, true); - - return null; -#endif - - throw NotImplementedInReferenceAssembly(); - } - - /// - /// Gets a folder, given its path. Returns null if the folder does not exist. - /// - /// The path to a folder, as returned from the property. - /// The cancellation token. - /// A folder for the specified path, or null if it does not exist. - public static async Task GetFolderFromPathAsync(string path, CancellationToken cancellationToken) - { - Requires.NotNullOrEmpty(path, "path"); - -#if DESKTOP || ANDROID || __IOS__ || MAC - await AwaitExtensions.SwitchOffMainThreadAsync(cancellationToken); - if (System.IO.Directory.Exists(path)) - return new FileSystemFolder(path, true); - - return null; -#endif - - throw NotImplementedInReferenceAssembly(); - } - } -}