namespace Bmp.Core.FFMpeg.CsCorePorts; /// /// Provides s for converting raw time values (e.g. bytes, samples,...) to a and back. /// public sealed class TimeConverterFactory { // ReSharper disable once InconsistentNaming private readonly static TimeConverterFactory _instance = new TimeConverterFactory(); /// /// Gets the default instance of the factory. /// public static TimeConverterFactory Instance { get { return _instance; } } private readonly Dictionary _timeConverters; private readonly Dictionary _cache; private TimeConverterFactory() { _timeConverters = new Dictionary(); _cache = new Dictionary(); RegisterTimeConverterForSourceType(TimeConverter.WaveSourceTimeConverter); RegisterTimeConverterForSourceType(TimeConverter.SampleSourceTimeConverter); } /// /// Registers a new for a specific source type. /// /// The to register. /// The source type. /// timeConverter is null. /// There is already a registered for the specified . /// The class uses the source type to find choose the best for an . For more information, see . public void RegisterTimeConverterForSourceType(TimeConverter timeConverter) where TSource : IAudioSource { if (timeConverter == null) throw new ArgumentNullException("timeConverter"); var type = typeof(TSource); if (_timeConverters.ContainsKey(type)) throw new ArgumentException("A timeconverter for the same source type got already registered."); _timeConverters.Add(type, timeConverter); } /// /// Unregisters a previously registered . /// /// The source type, that got passed to the method previously. /// The specified source type could not be found. public void UnregisterTimeConverter() where TSource : IAudioSource { var type = typeof(TSource); if (!_timeConverters.ContainsKey(type)) throw new ArgumentException("There is no timeconverter registered for the specified source type."); _timeConverters.Remove(type); } /// /// Gets the for the specified . /// /// The object to get the for. /// The type of the . /// The best for the specified . /// The specified is null. /// /// Specified type is no AudioSource.;type /// or /// No registered time converter for the specified source type was found. /// or /// Multiple possible time converters, for the specified source type, were found. Specify which time converter to use, through the . /// /// /// The chooses the best for the specified . /// If there is no applied to the object (the ), it looks up the inheritance hierarchy (interfaces included) of the object /// and searches for all registered source types. If there is a match it returns the associated . If there are more or less than one match BUT no /// it throws an exception. public TimeConverter GetTimeConverterForSource(TSource source) where TSource : class, IAudioSource { if (source == null) throw new ArgumentNullException("source"); return GetTimeConverterForSourceType(source.GetType()); } /// /// Gets the for the specified source type. /// /// The type of the source. /// The best for the specified source type. /// /// Specified type is no AudioSource.;type /// or /// No registered time converter for the specified source type was found. /// or /// Multiple possible time converters, for the specified source type, were found. Specify which time converter to use, through the . /// /// /// The chooses the best for the specified source type. /// If there is no applied to the object, it looks up the inheritance hierarchy (interfaces included) of the object /// and searches for all registered source types. If there is a match it returns the associated . If there are more or less than one match BUT no /// it throws an exception. public TimeConverter GetTimeConverterForSource() where TSource : IAudioSource { return GetTimeConverterForSourceType(typeof(TSource)); } /// /// Gets the for the specified . /// /// The to get the associated for. /// The best for the specified . /// /// Specified type is no AudioSource.;type /// or /// No registered time converter for the specified source type was found. /// or /// Multiple possible time converters, for the specified source type, were found. Specify which time converter to use, through the . /// /// /// The chooses the best for the specified . /// If there is no applied to the object (the ), it looks up the inheritance hierarchy (interfaces included) of the object /// and searches for all registered source types. If there is a match it returns the associated . If there are more or less than one match BUT no /// it throws an exception. public TimeConverter GetTimeConverterForSourceType(Type sourceType) { if (sourceType == null) throw new ArgumentNullException("sourceType"); if (!typeof(IAudioSource).IsAssignableFrom(sourceType)) throw new ArgumentException("Specified type is no AudioSource.", "sourceType"); //we may got it already in the cache if (_cache.ContainsKey(sourceType)) return _cache[sourceType].GetTimeConverter(); //nope, there is nothing in the cache //search for a TimeConverterAttribute var attribute = sourceType.GetCustomAttributes(typeof(TimeConverterAttribute), false).FirstOrDefault() as TimeConverterAttribute; TimeConverter timeConverter = null; try { if (attribute == null) { //there is no attribute //search for base types var baseTypes = GetTypes(sourceType).Where(x => _timeConverters.ContainsKey(x)).ToArray(); //we've got a match if (baseTypes.Length == 1) { timeConverter = _timeConverters[baseTypes.First()]; return timeConverter; } //we've got no match if (baseTypes.Length == 0) throw new ArgumentException( "No registered time converter for the specified source type was found."); //else baseTypes.Length > 1 throw new ArgumentException( "Multiple possible time converters, for the specified source type, were found. Specify which time converter to use, through the TimeConverterAttribute."); } var timeConverterType = attribute.TimeConverterType; timeConverter = (TimeConverter)Activator.CreateInstance(timeConverterType, attribute.Args); return timeConverter; } finally { //add the result to the cache if (timeConverter != null) { CacheItem cacheItem; if (attribute == null) cacheItem = new CacheItem { CreateNewInstance = false, TimeConverter = timeConverter }; else cacheItem = new CacheItem { CreateNewInstance = attribute.ForceNewInstance, TimeConverterAttribute = attribute, TimeConverter = attribute.ForceNewInstance ? null : timeConverter }; _cache[sourceType] = cacheItem; } } } /// /// Clears the internal cache. /// public void ClearCache() { _cache.Clear(); } private IEnumerable GetTypes(Type type) { //copied from dadhi see http://stackoverflow.com/questions/1823655/given-a-c-sharp-type-get-its-base-classes-and-implemented-interfaces return type.BaseType == typeof(object) ? type.GetInterfaces() : Enumerable .Repeat(type.BaseType, 1) .Concat(type.GetInterfaces()) .Concat(GetTypes(type.BaseType)) .Distinct(); } private class CacheItem { public TimeConverter TimeConverter { get; set; } public TimeConverterAttribute TimeConverterAttribute { get; set; } public bool CreateNewInstance { get; set; } public TimeConverter GetTimeConverter() { if (CreateNewInstance) return (TimeConverter)Activator.CreateInstance(TimeConverterAttribute.TimeConverterType, TimeConverterAttribute.Args); return TimeConverter; } } }