forked from filoe/cscore
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTimeConverterFactory.cs
242 lines (214 loc) · 12.2 KB
/
TimeConverterFactory.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
using System;
using System.Collections.Generic;
using System.Linq;
namespace CSCore
{
/// <summary>
/// Provides <see cref="TimeConverter"/>s for converting raw time values (e.g. bytes, samples,...) to a <see cref="TimeSpan"/> and back.
/// </summary>
public sealed class TimeConverterFactory
{
// ReSharper disable once InconsistentNaming
private readonly static TimeConverterFactory _instance = new TimeConverterFactory();
/// <summary>
/// Gets the default instance of the factory.
/// </summary>
public static TimeConverterFactory Instance
{
get { return _instance; }
}
private readonly Dictionary<Type, TimeConverter> _timeConverters;
private readonly Dictionary<Type, CacheItem> _cache;
private TimeConverterFactory()
{
_timeConverters = new Dictionary<Type, TimeConverter>();
_cache = new Dictionary<Type, CacheItem>();
RegisterTimeConverterForSourceType<IWaveSource>(TimeConverter.WaveSourceTimeConverter);
RegisterTimeConverterForSourceType<ISampleSource>(TimeConverter.SampleSourceTimeConverter);
}
/// <summary>
/// Registers a new <see cref="TimeConverter"/> for a specific source type.
/// </summary>
/// <param name="timeConverter">The <see cref="TimeConverter"/> to register.</param>
/// <typeparam name="TSource">The source type.</typeparam>
/// <exception cref="ArgumentNullException">timeConverter is null.</exception>
/// <exception cref="ArgumentException">There is already a <see cref="TimeConverter"/> registered for the specified <typeparamref name="TSource"/>.</exception>
/// <remarks>The <see cref="TimeConverterFactory"/> class uses the source type to find choose the best <see cref="TimeConverter"/> for an <see cref="IAudioSource"/>. For more information, see <see cref="GetTimeConverterForSourceType"/>.</remarks>
public void RegisterTimeConverterForSourceType<TSource>(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);
}
/// <summary>
/// Unregisters a previously registered <see cref="TimeConverter"/>.
/// </summary>
/// <typeparam name="TSource">The source type, that got passed to the <see cref="RegisterTimeConverterForSourceType{TSource}"/> method previously.</typeparam>
/// <exception cref="ArgumentException">The specified source type could not be found.</exception>
public void UnregisterTimeConverter<TSource>()
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);
}
/// <summary>
/// Gets the <see cref="TimeConverter"/> for the specified <paramref name="source"/>.
/// </summary>
/// <param name="source">The <see cref="IAudioSource"/> object to get the <see cref="TimeConverter"/> for.</param>
/// <typeparam name="TSource">The type of the <paramref name="source"/>.</typeparam>
/// <returns>The best <see cref="TimeConverter"/> for the specified <paramref name="source"/>.</returns>
/// <exception cref="ArgumentNullException">The specified <paramref name="source"/> is null.</exception>
/// <exception cref="System.ArgumentException">
/// 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 <see cref="TimeConverterAttribute"/>.
/// </exception>
/// <remarks>
/// The <see cref="GetTimeConverterForSource{TSource}(TSource)"/> chooses the best <see cref="TimeConverter"/> for the specified <paramref name="source"/>.
/// If there is no <see cref="TimeConverterAttribute"/> applied to the <see cref="IAudioSource"/> object (the <paramref name="source"/>), it looks up the inheritance hierarchy (interfaces included) of the <see cref="IAudioSource"/> object
/// and searches for all registered source types. If there is a match it returns the associated <see cref="TimeConverter"/>. If there are more or less than one match BUT no <see cref="TimeConverterAttribute"/>
/// it throws an exception.</remarks>
public TimeConverter GetTimeConverterForSource<TSource>(TSource source) where TSource : class, IAudioSource
{
if (source == null)
throw new ArgumentNullException("source");
return GetTimeConverterForSourceType(source.GetType());
}
/// <summary>
/// Gets the <see cref="TimeConverter"/> for the specified source type.
/// </summary>
/// <typeparam name="TSource">The type of the source.</typeparam>
/// <returns>The best <see cref="TimeConverter"/> for the specified source type.</returns>
/// <exception cref="System.ArgumentException">
/// 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 <see cref="TimeConverterAttribute"/>.
/// </exception>
/// <remarks>
/// The <see cref="GetTimeConverterForSource{TSource}()"/> chooses the best <see cref="TimeConverter"/> for the specified source type.
/// If there is no <see cref="TimeConverterAttribute"/> applied to the <see cref="IAudioSource"/> object, it looks up the inheritance hierarchy (interfaces included) of the <see cref="IAudioSource"/> object
/// and searches for all registered source types. If there is a match it returns the associated <see cref="TimeConverter"/>. If there are more or less than one match BUT no <see cref="TimeConverterAttribute"/>
/// it throws an exception.</remarks>
public TimeConverter GetTimeConverterForSource<TSource>()
where TSource : IAudioSource
{
return GetTimeConverterForSourceType(typeof (TSource));
}
/// <summary>
/// Gets the <see cref="TimeConverter"/> for the specified <paramref name="sourceType"/>.
/// </summary>
/// <param name="sourceType">The <see cref="Type"/> to get the associated <see cref="TimeConverter"/> for.</param>
/// <returns>The best <see cref="TimeConverter"/> for the specified <paramref name="sourceType"/>.</returns>
/// <exception cref="System.ArgumentException">
/// 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 <see cref="TimeConverterAttribute"/>.
/// </exception>
/// <remarks>
/// The <see cref="GetTimeConverterForSourceType(Type)"/> chooses the best <see cref="TimeConverter"/> for the specified <paramref name="sourceType"/>.
/// If there is no <see cref="TimeConverterAttribute"/> applied to the <see cref="IAudioSource"/> object (the <paramref name="sourceType"/>), it looks up the inheritance hierarchy (interfaces included) of the <see cref="IAudioSource"/> object
/// and searches for all registered source types. If there is a match it returns the associated <see cref="TimeConverter"/>. If there are more or less than one match BUT no <see cref="TimeConverterAttribute"/>
/// it throws an exception.</remarks>
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;
}
}
}
/// <summary>
/// Clears the internal cache.
/// </summary>
public void ClearCache()
{
_cache.Clear();
}
private IEnumerable<Type> 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;
}
}
}
}