/// <summary> /// Sometimes a "file" actually exists as multiple files in a directory. This method will open a "file" using the given directory. /// </summary> /// <param name="path">Path of the directory to open</param> /// <param name="duplicateFileTypeSelector">Resolves duplicate file type detections</param> /// <param name="manager">Instance of the current plugin manager</param> /// <returns>An object representing the given directory, or null if no such object could be found</returns> /// <exception cref="ArgumentNullException">Thrown if <see cref="file"/>, <paramref name="duplicateDirectoryTypeSelector"/>, or <paramref name="manager"/> is null.</exception> public static async Task <object> OpenDirectory(string path, DuplicateMatchSelector duplicateDirectoryTypeSelector, PluginManager manager) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException(nameof(path)); } if (duplicateDirectoryTypeSelector == null) { throw new ArgumentNullException(nameof(duplicateDirectoryTypeSelector)); } if (manager == null) { throw new ArgumentNullException(nameof(manager)); } var type = await GetDirectoryType(path, duplicateDirectoryTypeSelector, manager); var openers = manager.GetRegisteredObjects <IFileOpener>().Where(x => x.SupportsType(type)); if (type == null || !openers.Any()) { // Nothing can model the file return(null); } else { return(await openers.OrderBy(x => x.GetUsagePriority(type)).First().OpenFile(type, path, manager.CurrentFileSystem)); } }
/// <summary> /// Using the given file, auto-detects the file type and creates an instance of an appropriate object to model it. /// If no such class could be found, will return the original file /// </summary> /// <param name="file">The file for which to find a better class</param> /// <param name="duplicateFileTypeSelector">Resolves duplicate file type detections</param> /// <param name="manager">Instance of the current plugin manager</param> /// <returns>An object that represents the given file, or <paramref name="file"/> if no such class could be found.</returns> /// <exception cref="ArgumentNullException">Thrown if <see cref="file"/>, <paramref name="duplicateFileTypeSelector"/>, or <paramref name="manager"/> is null.</exception> public static async Task <object> OpenFile(GenericFile file, DuplicateMatchSelector duplicateFileTypeSelector, PluginManager manager) { if (file == null) { throw new ArgumentNullException(nameof(file)); } if (duplicateFileTypeSelector == null) { throw new ArgumentNullException(nameof(duplicateFileTypeSelector)); } if (manager == null) { throw new ArgumentNullException(nameof(manager)); } var type = await GetFileType(file, duplicateFileTypeSelector, manager); var fileOpeners = manager.GetRegisteredObjects <IFileOpener>().Where(x => x.SupportsType(type)); var genericFileOpeners = manager.GetRegisteredObjects <IFileFromGenericFileOpener>().Where(x => x.SupportsType(type)); if (type == null || !(fileOpeners.Any() || genericFileOpeners.Any()) ) { // Nothing can model the file // Re-open GenericFile so it's not readonly var newFile = new GenericFile(); await newFile.OpenFile(file.Filename, manager.CurrentFileSystem); return(newFile); } else { var openers = new List <IBaseFileOpener>(); openers.AddRange(fileOpeners); openers.AddRange(genericFileOpeners); var fileOpener = openers.OrderByDescending(x => x.GetUsagePriority(type)).First(); if (fileOpener is IFileOpener fromFileOpener) { return(await fromFileOpener.OpenFile(type, file.Filename, manager.CurrentFileSystem)); } else if (fileOpener is IFileFromGenericFileOpener fromGenericFileOpener) { return(await fromGenericFileOpener.OpenFile(type, file)); } else { throw new Exception("Unsupported IBaseFileOpener type: " + fileOpener.GetType().Name); } } }
/// <summary> /// Gets a type that can represent the given directory /// </summary> /// <param name="path">Directory for which to detect the type</param> /// <param name="duplicateDirectoryTypeSelector">Resolves duplicate file type detections</param> /// <param name="manager">Instance of the current plugin manager</param> /// <returns>A type that can represent the given file</returns> /// <exception cref="ArgumentNullException">Thrown if <see cref="path"/>, <paramref name="duplicateDirectoryTypeSelector"/>, or <paramref name="manager"/> is null.</exception> public static async Task <TypeInfo> GetDirectoryType(string path, DuplicateMatchSelector duplicateDirectoryTypeSelector, PluginManager manager) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException(nameof(path)); } if (duplicateDirectoryTypeSelector == null) { throw new ArgumentNullException(nameof(duplicateDirectoryTypeSelector)); } if (manager == null) { throw new ArgumentNullException(nameof(manager)); } var resultSetTasks = new List <Task <IEnumerable <FileTypeDetectionResult> > >(); foreach (var detector in manager.GetRegisteredObjects <IDirectoryTypeDetector>()) { // Start the file type detection var detectTask = detector.DetectDirectoryType(path, manager); // Add the task to a list of running detection tasks, so there is the option of running them asynchronously. resultSetTasks.Add(detectTask); } var matches = new List <FileTypeDetectionResult>(); // Merge all results into one list foreach (var item in await Task.WhenAll(resultSetTasks)) { matches.AddRange(item); } return(GetCorrectFileTypeDetectionResult(matches, duplicateDirectoryTypeSelector)?.FileType); }
/// <summary> /// Opens a file, auto-detecting the correct type /// </summary> /// <param name="path">Path of the file to open</param> /// <param name="duplicateFileTypeSelector">Resolves duplicate file type detections</param> /// <param name="manager">Instance of the current plugin manager</param> /// <returns>An object that represents the file at the given path</returns> /// <exception cref="ArgumentNullException">Thrown if <see cref="file"/>, <paramref name="duplicateFileTypeSelector"/>, or <paramref name="manager"/> is null.</exception> /// <exception cref="FileNotFoundException">Thrown if no file or directory could be found at the given path</exception> public static async Task <object> OpenFile(string path, DuplicateMatchSelector duplicateFileTypeSelector, PluginManager manager) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException(nameof(path)); } if (duplicateFileTypeSelector == null) { throw new ArgumentNullException(nameof(duplicateFileTypeSelector)); } if (manager == null) { throw new ArgumentNullException(nameof(manager)); } if (manager.CurrentFileSystem.FileExists(path)) { using (var file = new GenericFile()) { file.IsReadOnly = true; await file.OpenFile(path, manager.CurrentFileSystem); return(await OpenFile(file, duplicateFileTypeSelector, manager)); } } else if (manager.CurrentFileSystem.DirectoryExists(path)) { return(OpenDirectory(path, duplicateFileTypeSelector, manager)); } else { throw new FileNotFoundException(Properties.Resources.IO_FileNotFound, path); } }
/// <summary> /// Gets the correct file type detection result /// </summary> /// <param name="results">The results from which to select</param> /// <param name="duplicateMatchSelector">A function that can select between duplicate types</param> /// <returns>The correct file type detection result, or null if there are no results</returns> private static FileTypeDetectionResult GetCorrectFileTypeDetectionResult(IEnumerable <FileTypeDetectionResult> results, DuplicateMatchSelector duplicateMatchSelector) { if (!results.Any()) { return(null); } else if (results.Count() == 1) { return(results.First()); } else { // Multiple matches exist. Find the one with the highest chance of being the correct one. var maxChance = results.Max(x => x.MatchChance); var topMatches = results.Where(x => x.MatchChance == maxChance); if (!topMatches.Any()) { // Nothing matches the maximum. Should be unreachable. throw new Exception("Could not find results that match the maximum. This exception should have been unreachable and likely indicates an error in implementation."); } else if (topMatches.Count() == 1) { return(topMatches.First()); } else { return(duplicateMatchSelector(topMatches)); } } }
/// <summary> /// Gets a type that can represent the given file /// </summary> /// <param name="file">File for which to detect the type</param> /// <param name="duplicateFileTypeSelector">Resolves duplicate file type detections</param> /// <param name="manager">Instance of the current plugin manager</param> /// <returns>A type that can represent the given file</returns> /// <exception cref="ArgumentNullException">Thrown if <see cref="file"/>, <paramref name="duplicateFileTypeSelector"/>, or <paramref name="manager"/> is null.</exception> public static async Task <TypeInfo> GetFileType(GenericFile file, DuplicateMatchSelector duplicateFileTypeSelector, PluginManager manager) { if (file == null) { throw new ArgumentNullException(nameof(file)); } if (duplicateFileTypeSelector == null) { throw new ArgumentNullException(nameof(duplicateFileTypeSelector)); } if (manager == null) { throw new ArgumentNullException(nameof(manager)); } var resultSetTasks = new List <Task <IEnumerable <FileTypeDetectionResult> > >(); foreach (var detector in manager.GetRegisteredObjects <IFileTypeDetector>()) { // Start the file type detection var detectTask = Task.Run(async() => { try { return(await detector.DetectFileType(file, manager)); } catch (Exception ex) { Debug.WriteLine($"Encountered exception when using {detector.GetType().Name} to detect a file type: {ex.ToString()}"); return(new FileTypeDetectionResult[] { }); } }); // Add the task to a list of running detection tasks, so there is the option of running them asynchronously. resultSetTasks.Add(detectTask); // However, the file isn't necessarily thread-safe, so if it isn't, only one should be run at any one time if (!file.IsThreadSafe) { try { await detectTask; } catch (Exception ex) { Debug.WriteLine($"Encountered exception when using {detector.GetType().Name} to detect a file type: {ex.ToString()}"); continue; } } } var matches = new List <FileTypeDetectionResult>(); // Merge all results into one list foreach (var item in await Task.WhenAll(resultSetTasks)) { matches.AddRange(item); } return(GetCorrectFileTypeDetectionResult(matches, duplicateFileTypeSelector)?.FileType); }