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;}
}
}