Last active
August 29, 2015 14:05
-
-
Save dbeattie71/dad8d2338ac63b8d4e13 to your computer and use it in GitHub Desktop.
PCLStorage stuff
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using PCLStorage; | |
namespace Flashlist.Common | |
{ | |
//https://github.com/mono/mono/blob/master/mcs/class/corlib/System.IO/Path.cs | |
public static class PortablePathEx | |
{ | |
public static string ChangeExtension(string path, string extension) | |
{ | |
if (path == null) | |
return null; | |
if (path.IndexOfAny(InvalidPathChars) != -1) | |
throw new ArgumentException("Illegal characters in path."); | |
var iExt = findExtension(path); | |
if (extension == null) | |
return iExt < 0 ? path : path.Substring(0, iExt); | |
if (extension.Length == 0) | |
return iExt < 0 ? path + '.' : path.Substring(0, iExt + 1); | |
if (path.Length != 0) | |
{ | |
if (extension.Length > 0 && extension[0] != '.') | |
extension = "." + extension; | |
} | |
else | |
extension = String.Empty; | |
if (iExt < 0) | |
return path + extension; | |
if (iExt > 0) | |
{ | |
var temp = path.Substring(0, iExt); | |
return temp + extension; | |
} | |
return extension; | |
} | |
public static string Combine(string path1, string path2) | |
{ | |
if (path1 == null) | |
throw new ArgumentNullException("path1"); | |
if (path2 == null) | |
throw new ArgumentNullException("path2"); | |
if (path1.Length == 0) | |
return path2; | |
if (path2.Length == 0) | |
return path1; | |
if (path1.IndexOfAny(InvalidPathChars) != -1) | |
throw new ArgumentException("Illegal characters in path."); | |
if (path2.IndexOfAny(InvalidPathChars) != -1) | |
throw new ArgumentException("Illegal characters in path."); | |
//TODO???: UNC names | |
if (IsPathRooted(path2)) | |
return path2; | |
var p1end = path1[path1.Length - 1]; | |
if (p1end != DirectorySeparatorChar && p1end != AltDirectorySeparatorChar && p1end != VolumeSeparatorChar) | |
return path1 + DirectorySeparatorStr + path2; | |
return path1 + path2; | |
} | |
public static string GetDirectoryName(string path) | |
{ | |
// LAMESPEC: For empty string MS docs say both | |
// return null AND throw exception. Seems .NET throws. | |
if (path == String.Empty) | |
throw new ArgumentException("Invalid path"); | |
if (path == null || GetPathRoot(path) == path) | |
return null; | |
if (path.Trim().Length == 0) | |
throw new ArgumentException("Argument string consists of whitespace characters only."); | |
if (path.IndexOfAny(InvalidPathChars) > -1) | |
throw new ArgumentException("Path contains invalid characters"); | |
var nLast = path.LastIndexOfAny(PathSeparatorChars); | |
if (nLast == 0) | |
nLast++; | |
if (nLast > 0) | |
{ | |
var ret = path.Substring(0, nLast); | |
var l = ret.Length; | |
if (l >= 2 && DirectorySeparatorChar == '\\' && ret[l - 1] == VolumeSeparatorChar) | |
return ret + DirectorySeparatorChar; | |
if (l == 1 && DirectorySeparatorChar == '\\' && path.Length >= 2 && path[nLast] == VolumeSeparatorChar) | |
return ret + VolumeSeparatorChar; | |
// | |
// Important: do not use CanonicalizePath here, use | |
// the custom CleanPath here, as this should not | |
// return absolute paths | |
// | |
return CleanPath(ret); | |
} | |
return String.Empty; | |
} | |
public static string GetExtension(string path) | |
{ | |
if (path == null) | |
return null; | |
if (path.IndexOfAny(InvalidPathChars) != -1) | |
throw new ArgumentException("Illegal characters in path."); | |
var iExt = findExtension(path); | |
if (iExt > -1) | |
if (iExt < path.Length - 1) | |
return path.Substring(iExt); | |
return string.Empty; | |
} | |
public static string GetFileName(string path) | |
{ | |
if (path == null || path.Length == 0) | |
return path; | |
if (path.IndexOfAny(InvalidPathChars) != -1) | |
throw new ArgumentException("Illegal characters in path."); | |
var nLast = path.LastIndexOfAny(PathSeparatorChars); | |
if (nLast >= 0) | |
return path.Substring(nLast + 1); | |
return path; | |
} | |
public static string GetFileNameWithoutExtension(string path) | |
{ | |
return ChangeExtension(GetFileName(path), null); | |
} | |
public static string GetPathRoot(string path) | |
{ | |
if (path == null) | |
return null; | |
if (path.Trim().Length == 0) | |
throw new ArgumentException("The specified path is not of a legal form."); | |
if (!IsPathRooted(path)) | |
return String.Empty; | |
if (DirectorySeparatorChar == '/') | |
// UNIX | |
return IsDsc(path[0]) ? DirectorySeparatorStr : String.Empty; | |
// Windows | |
var len = 2; | |
if (path.Length == 1 && IsDsc(path[0])) | |
return DirectorySeparatorStr; | |
if (path.Length < 2) | |
return String.Empty; | |
if (IsDsc(path[0]) && IsDsc(path[1])) | |
{ | |
// UNC: \\server or \\server\share | |
// Get server | |
while (len < path.Length && !IsDsc(path[len])) len++; | |
// Get share | |
if (len < path.Length) | |
{ | |
len++; | |
while (len < path.Length && !IsDsc(path[len])) len++; | |
} | |
return DirectorySeparatorStr + | |
DirectorySeparatorStr + | |
path.Substring(2, len - 2).Replace(AltDirectorySeparatorChar, DirectorySeparatorChar); | |
} | |
if (IsDsc(path[0])) | |
// path starts with '\' or '/' | |
return DirectorySeparatorStr; | |
if (path[1] == VolumeSeparatorChar) | |
{ | |
// C:\folder | |
if (path.Length >= 3 && (IsDsc(path[2]))) len++; | |
} | |
else | |
// return Directory.GetCurrentDirectory().Substring(0, 2);// + path.Substring (0, len); | |
return GetCurrentDirectory().Substring(0, 2); // + path.Substring (0, len); | |
return path.Substring(0, len); | |
} | |
public static bool IsPathRooted(string path) | |
{ | |
if (path == null || path.Length == 0) | |
return false; | |
if (path.IndexOfAny(InvalidPathChars) != -1) | |
throw new ArgumentException("Illegal characters in path."); | |
var c = path[0]; | |
return (c == DirectorySeparatorChar || | |
c == AltDirectorySeparatorChar || | |
(!dirEqualsVolume && path.Length > 1 && path[1] == VolumeSeparatorChar)); | |
} | |
public static char[] GetInvalidPathChars() | |
{ | |
// return a new array as we do not want anyone to be able to change the values | |
//if (Environment.IsRunningOnWindows) { | |
return new char[36] | |
{ | |
'\x22', '\x3C', '\x3E', '\x7C', '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', | |
'\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F', '\x10', '\x11', '\x12', | |
'\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D', | |
'\x1E', '\x1F' | |
}; | |
//} else { | |
// return new char [1] { '\x00' }; | |
//} | |
} | |
private static bool IsDsc(char c) | |
{ | |
return c == DirectorySeparatorChar || c == AltDirectorySeparatorChar; | |
} | |
private static string GetCurrentDirectory() | |
{ | |
return FileSystem.Current.LocalStorage.Path; | |
} | |
private static int findExtension(string path) | |
{ | |
// method should return the index of the path extension | |
// start or -1 if no valid extension | |
if (path != null) | |
{ | |
var iLastDot = path.LastIndexOf('.'); | |
var iLastSep = path.LastIndexOfAny(PathSeparatorChars); | |
if (iLastDot > iLastSep) | |
return iLastDot; | |
} | |
return -1; | |
} | |
public static readonly char[] InvalidPathChars; | |
public static readonly char AltDirectorySeparatorChar; | |
public static readonly char DirectorySeparatorChar; | |
public static readonly char VolumeSeparatorChar; | |
public static readonly char PathSeparator; | |
internal static readonly string DirectorySeparatorStr; | |
internal static readonly char[] PathSeparatorChars; | |
private static readonly bool dirEqualsVolume; | |
internal static string CleanPath(string s) | |
{ | |
var l = s.Length; | |
var sub = 0; | |
var start = 0; | |
// Host prefix? | |
var s0 = s[0]; | |
if (l > 2 && s0 == '\\' && s[1] == '\\') start = 2; | |
// We are only left with root | |
if (l == 1 && (s0 == DirectorySeparatorChar || s0 == AltDirectorySeparatorChar)) | |
return s; | |
// Cleanup | |
for (var i = start; i < l; i++) | |
{ | |
var c = s[i]; | |
if (c != DirectorySeparatorChar && c != AltDirectorySeparatorChar) | |
continue; | |
if (i + 1 == l) | |
sub++; | |
else | |
{ | |
c = s[i + 1]; | |
if (c == DirectorySeparatorChar || c == AltDirectorySeparatorChar) | |
sub++; | |
} | |
} | |
if (sub == 0) | |
return s; | |
var copy = new char[l - sub]; | |
if (start != 0) | |
{ | |
copy[0] = '\\'; | |
copy[1] = '\\'; | |
} | |
for (int i = start, j = start; i < l && j < copy.Length; i++) | |
{ | |
var c = s[i]; | |
if (c != DirectorySeparatorChar && c != AltDirectorySeparatorChar) | |
{ | |
copy[j++] = c; | |
continue; | |
} | |
// For non-trailing cases. | |
if (j + 1 != copy.Length) | |
{ | |
copy[j++] = DirectorySeparatorChar; | |
for (; i < l - 1; i++) | |
{ | |
c = s[i + 1]; | |
if (c != DirectorySeparatorChar && c != AltDirectorySeparatorChar) | |
break; | |
} | |
} | |
} | |
return new String(copy); | |
} | |
static PortablePathEx() | |
{ | |
//VolumeSeparatorChar = MonoIO.VolumeSeparatorChar; | |
//DirectorySeparatorChar = MonoIO.DirectorySeparatorChar; | |
//AltDirectorySeparatorChar = MonoIO.AltDirectorySeparatorChar; | |
VolumeSeparatorChar = ':'; | |
DirectorySeparatorChar = '\\'; | |
AltDirectorySeparatorChar = '/'; | |
//PathSeparator = MonoIO.PathSeparator; | |
PathSeparator = ';'; | |
// this copy will be modifiable ("by design") | |
InvalidPathChars = GetInvalidPathChars(); | |
// internal fields | |
DirectorySeparatorStr = DirectorySeparatorChar.ToString(); | |
PathSeparatorChars = new[] | |
{ | |
DirectorySeparatorChar, | |
AltDirectorySeparatorChar, | |
VolumeSeparatorChar | |
}; | |
dirEqualsVolume = (DirectorySeparatorChar == VolumeSeparatorChar); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Threading.Tasks; | |
using Flashlist.Common; | |
using PCLStorage; | |
namespace Flashlist.Services | |
{ | |
public interface IStorageService | |
{ | |
Task<TryResult<string>> TryReadTextFileAsync(string path); | |
Task WriteFileAsync(string path, string contents); | |
} | |
public class StorageService : IStorageService | |
{ | |
public async Task<TryResult<string>> TryReadTextFileAsync(string path) | |
{ | |
var content = ""; | |
var operationSucceeded = await TryReadFileCommonAsync(path, async stream => | |
{ | |
using (var reader = new StreamReader(stream)) | |
{ | |
content = await reader.ReadToEndAsync().ConfigureAwait(false); | |
return true; | |
} | |
}).ConfigureAwait(false); | |
return TryResult.Create(operationSucceeded, content); | |
} | |
public async Task WriteFileAsync(string path, string contents) | |
{ | |
await WriteFileCommonAsync(path, async stream => | |
{ | |
using (var writer = new StreamWriter(stream)) | |
{ | |
await writer.WriteAsync(contents).ConfigureAwait(false); | |
} | |
}).ConfigureAwait(false); | |
} | |
private static async Task<bool> TryReadFileCommonAsync(string path, Func<Stream, Task<bool>> streamAction) | |
{ | |
var fullPath = FullPath(path); | |
var file = await FileSystem.GetFileFromPathAsync(fullPath); | |
if (file == null) | |
return false; | |
return await streamAction(await file.OpenAsync(FileAccess.Read)).ConfigureAwait(false); | |
} | |
private static string FullPath(string path) | |
{ | |
return PortablePath.Combine(PCLStorage.FileSystem.Current.LocalStorage.Path, path); | |
} | |
private async Task WriteFileCommonAsync(string path, Func<Stream, Task> streamAction) | |
{ | |
var fullPath = FullPath(path); | |
var file = await FileSystem.GetFileFromPathAsync(fullPath); | |
if (file != null) | |
await file.DeleteAsync(); | |
var folderPath = PortablePathEx.GetDirectoryName(fullPath); | |
var folder = await FileSystem.GetFolderFromPathAsync(folderPath); | |
if (folder == null) | |
throw new Exception("Folder does not exist."); | |
var filename = PortablePathEx.GetFileName(path); | |
var newFile = await folder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting); | |
using (var fileStream = await newFile.OpenAsync(FileAccess.ReadAndWrite)) | |
{ | |
await streamAction(fileStream).ConfigureAwait(false); | |
} | |
} | |
private static readonly IFileSystem FileSystem = PCLStorage.FileSystem.Current; | |
} | |
public static class TryResult | |
{ | |
public static TryResult<TResult> Create<TResult>(bool operationSucceeded, TResult result) | |
{ | |
return new TryResult<TResult>(operationSucceeded, result); | |
} | |
} | |
public class TryResult<TResult> | |
{ | |
public bool OperationSucceeded { get; private set; } | |
public TResult Result { get; private set; } | |
public TryResult(bool operationSucceeded, TResult result = default(TResult)) | |
{ | |
OperationSucceeded = operationSucceeded; | |
Result = result; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment