using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace Chernobyl.Resources { /// /// Used to cache resources that were loaded in from a FileStream so that /// they can be retrieved quickly on the next call to From(). This is done /// by mapping the filepath from the FileStream to the loaded in resource. /// /// The resource type to load in. public class FileStreamResourceMapper : ResourceProcessor { /// /// Constructor. /// public FileStreamResourceMapper() { // On Windows, we want case-insensitive comparisons but not on Unix // where case sensitivity is expected. if (Environment.OSVersion.Platform != PlatformID.Unix) ResourceMap = new Dictionary(StringComparer.OrdinalIgnoreCase); else ResourceMap = new Dictionary(); } /// /// Attempts to cast the passed in to a /// and uses the file path in /// to locate and return the resource in /// case it has already been processed. /// /// The callback that will be invoked when the /// resource has been loaded. /// The stream to load the resource from. /// Options for the loading or processing of the /// resource. This does not /// currently use any options. public override void From(ResourceProcessCallback callback, Stream readFrom, params IOption[] options) { // try to retrieve the resource FileStream file = readFrom as FileStream; if (file != null) { // We use Path.GetFullPath(String) to ensure the names stored // and compared to in the map are all full paths. This avoids // any issues where you reference the same file but with different // string values. Additionally, we cut out any trailing slashes // as Path.GetFullPath(String) doesn't seem to standardize on // trailing slashes very well. See here for more details: // http://stackoverflow.com/questions/2281531/how-can-i-compare-directory-paths-in-c String filePath = Path.GetFullPath(file.Name).TrimEnd('\\'); // If we have a ResourceName append that to the end of the file // path so we can properly differentiate between resources that // belong to the same file but have different names. ResourceName resourceName = options.OfType().FirstOrDefault(); if (resourceName != null) filePath = filePath + resourceName.Name; TResource resource; if (ResourceMap.TryGetValue(filePath, out resource) == true) callback(new ResourceProcessResult(resource, null, true, true)); else { // the resource was not in the map so we are going to allow // the other resource processors to process it and map the result. // To do this, we will create a callback wrapper around the // callback we received. ResourceProcessCallback addResource = result => { ResourceMap.Add(filePath, result.Resource); callback(result); }; CheckedNextFrom(addResource, readFrom, options); } } else { // the stream passed in is not a FileStream so we just allow the other processors // to work their magic CheckedNextFrom(callback, readFrom, options); } } /// /// Passes on processing to the next resource. /// /// The resource to write to the stream. /// The stream to have the resource written to. /// Options for the writing or processing of the resource. public override void To(TResource resource, Stream writeTo, params IOption[] options) { CheckedNextTo(resource, writeTo, options); } /// /// Holds the resources and the file paths that they were loaded from. /// Dictionary ResourceMap {get; set;} } }