[runtime] Fix corlib out of date error with disabled COM
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Base / Core / ServiceManager.cs
1 namespace System.Activities.Presentation
2 {
3
4     using System.Activities.Presentation.Internal.Properties;
5     using System;
6     using System.Collections;
7     using System.Collections.Generic;
8     using System.Diagnostics;
9     using System.Diagnostics.CodeAnalysis;
10     using System.Globalization;
11     using System.Activities.Presentation;
12
13     /// <summary>
14     /// The service manager implements IServiceProvider and provides access 
15     /// to services offered by the editing context. 
16     /// </summary>
17     /// Suppressing FxCop from complaining about our use of naming, since it has been approved
18     [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
19     public abstract class ServiceManager : IServiceProvider, IEnumerable<Type>
20     {
21
22         /// <summary>
23         /// Creates a new ServiceManager.
24         /// </summary>
25         protected ServiceManager() { }
26
27         /// <summary>
28         /// Returns true if the service manager contains a service of the given type.
29         /// </summary>
30         /// <param name="serviceType">The type of service to check.</param>
31         /// <returns>True if a service of type serviceType has been published.</returns>
32         /// <exception cref="ArgumentNullException">if serviceType is null.</exception>
33         public abstract bool Contains(Type serviceType);
34
35         /// <summary>
36         /// Returns true if the service manager contains a service of the given type.
37         /// </summary>
38         /// <typeparam name="TServiceType">The type of service to check.</typeparam>
39         /// <returns>True if a service of type TServiceType has been published.</returns>
40         [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
41         public bool Contains<TServiceType>()
42         {
43             return Contains(typeof(TServiceType));
44         }
45
46         /// <summary>
47         /// Retrives the requested service.  Unlike GetService, GetRequiredService 
48         /// throws a NotSupportedException if the service isn’t available.  The reason 
49         /// we provide this method, and not a normal GetService method is our wish to 
50         /// move services to a more reliable contract.
51         /// </summary>
52         /// <typeparam name="TServiceType">The type of service to retrieve.</typeparam>
53         /// <returns>An instance of the service.  This never returns null.</returns>
54         /// <exception cref="NotSupportedException">if there is no service of the given type.</exception>
55         [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
56         [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
57         public TServiceType GetRequiredService<TServiceType>()
58         {
59             TServiceType service = GetService<TServiceType>();
60             if (service == null)
61             {
62                 throw FxTrace.Exception.AsError(new NotSupportedException(
63                     string.Format(CultureInfo.CurrentCulture,
64                     Resources.Error_RequiredService, typeof(TServiceType).FullName)));
65             }
66             return service;
67         }
68
69         /// <summary>
70         /// Retrives the requested service.  This method returns null if the service could not be located.
71         /// </summary>
72         /// <typeparam name="TServiceType">The type of service to retrieve.</typeparam>
73         /// <returns>An instance of the service, or null if the service has not been published.</returns>
74         [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
75         public TServiceType GetService<TServiceType>()
76         {
77             object service = GetService(typeof(TServiceType));
78             return (TServiceType)service;
79         }
80
81         /// <summary>
82         /// Retrives the requested service.  This method returns null if the service could not be located.
83         /// </summary>
84         /// <param name="serviceType">The type of service to retrieve.</param>
85         /// <returns>An instance of the service, or null if the service has not been published.</returns>
86         /// <exception cref="ArgumentNullException">If serviceType is null.</exception>
87         public abstract object GetService(Type serviceType);
88
89         /// <summary>
90         /// Retrives an enumerator that can be used to enumerate all of the services that this 
91         /// service manager publishes.
92         /// </summary>
93         /// <returns>An enumeration of published services.</returns>
94         public abstract IEnumerator<Type> GetEnumerator();
95
96         /// <summary>
97         /// Calls back on the provided callback when someone has published the requested service.  
98         /// If the service was already available, this method invokes the callback immediately.
99         /// 
100         /// A generic version of this method is provided for convenience, and calls the non-generic 
101         /// method with appropriate casts.
102         /// </summary>
103         /// <param name="serviceType">The type of service to subscribe to.</param>
104         /// <param name="callback">A callback that will be notified when the service is available.</param>
105         /// <exception cref="ArgumentNullException">If serviceType or callback is null.</exception>
106         public abstract void Subscribe(Type serviceType, SubscribeServiceCallback callback);
107
108         /// <summary>
109         /// Calls back on the provided callback when someone has published the requested service.  
110         /// If the service was already available, this method invokes the callback immediately.
111         /// 
112         /// A generic version of this method is provided for convenience, and calls the non-generic 
113         /// method with appropriate casts.
114         /// </summary>
115         /// <typeparam name="TServiceType">The type of service to subscribe.</typeparam>
116         /// <param name="callback">A callback that will be invoked when the service is available.</param>
117         /// <exception cref="ArgumentNullException">If callback is null.</exception>
118         [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
119         public void Subscribe<TServiceType>(SubscribeServiceCallback<TServiceType> callback)
120         {
121             if (callback == null) throw FxTrace.Exception.ArgumentNull("callback");
122
123             // Call the standard Subscribe method and use a generic proxy
124             SubscribeProxy<TServiceType> proxy = new SubscribeProxy<TServiceType>(callback);
125             Subscribe(typeof(TServiceType), proxy.Callback);
126         }
127
128         /// <summary>
129         /// Publishes the given service type, but does not declare an instance yet.  When someone
130         /// requests the service the PublishServiceCallback will be invoked to create the instance.
131         /// The callback is only invoked once and after that the instance it returned is cached.
132         /// 
133         /// A generic version of this method is provided for convenience, and calls the non-generic 
134         /// method with appropriate casts.
135         /// </summary>
136         /// <param name="serviceType">The type of service to publish.</param>
137         /// <param name="callback">A callback that will be invoked when an instance of the service is needed.</param>
138         /// <exception cref="ArgumentNullException">If serviceType or callback is null.</exception>
139         /// <exception cref="ArgumentException">If serviceType has already been published.</exception>
140         public abstract void Publish(Type serviceType, PublishServiceCallback callback);
141
142         /// <summary>
143         /// Publishes the given service.  Once published, the service instance remains in the
144         /// service manager until the editing context is disposed.
145         /// </summary>
146         /// <param name="serviceType">The type of service to publish.</param>
147         /// <param name="serviceInstance">An instance of the service.</param>
148         /// <exception cref="ArgumentNullException">If serviceType or serviceInstance is null.</exception>
149         /// <exception cref="ArgumentException">If serviceInstance does not derive from or implement serviceType, or if serviceType has already been published.</exception>
150         public abstract void Publish(Type serviceType, object serviceInstance);
151
152         /// <summary>
153         /// Publishes the given service type, but does not declare an instance yet.  When someone
154         /// requests the service the PublishServiceCallback will be invoked to create the instance.
155         /// The callback is only invoked once and after that the instance it returned is cached.
156         /// </summary>
157         /// <typeparam name="TServiceType">The type of service to publish.</typeparam>
158         /// <param name="callback">A callback to be invoked when the service is required.</param>
159         /// <exception cref="ArgumentNullException">If callback is null.</exception>
160         /// <exception cref="ArgumentException">If TServiceType has already been published.</exception>
161         [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
162         public void Publish<TServiceType>(PublishServiceCallback<TServiceType> callback)
163         {
164             if (callback == null) throw FxTrace.Exception.ArgumentNull("callback");
165
166             // Call the standard Subscribe method and use a generic proxy
167             PublishProxy<TServiceType> proxy = new PublishProxy<TServiceType>(callback);
168             Publish(typeof(TServiceType), proxy.Callback);
169         }
170
171         /// <summary>
172         /// Publishes the given service.  Once published, the service instance remains in the
173         /// service manager until the editing context is disposed.
174         /// </summary>
175         /// <typeparam name="TServiceType">The type of service to publish.</typeparam>
176         /// <param name="serviceInstance">The instance of the service to publish.</param>
177         /// <exception cref="ArgumentException">If TServiceType has already been published.</exception>
178         public void Publish<TServiceType>(TServiceType serviceInstance)
179         {
180             if (serviceInstance == null) throw FxTrace.Exception.ArgumentNull("serviceInstance");
181             Publish(typeof(TServiceType), serviceInstance);
182         }
183
184         /// <summary>
185         /// Removes a subscription for the TServiceType.
186         /// </summary>
187         /// <typeparam name="TServiceType">The type of service to remove the subscription from.</typeparam>
188         /// <param name="callback">The callback object to remove from the subscription.</param>
189         /// <exception cref="ArgumentNullException">If callback is null.</exception>
190         [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
191         public void Unsubscribe<TServiceType>(SubscribeServiceCallback<TServiceType> callback)
192         {
193             if (callback == null) throw FxTrace.Exception.ArgumentNull("callback");
194             SubscribeProxy<TServiceType> proxy = new SubscribeProxy<TServiceType>(callback);
195             Unsubscribe(typeof(TServiceType), proxy.Callback);
196         }
197
198         /// <summary>
199         /// Removes a subscription for the serviceType.
200         /// </summary>
201         /// <param name="serviceType">The type of service to remove the subscription from.</param>
202         /// <param name="callback">The callback object to remove from the subscription.</param>
203         /// <exception cref="ArgumentNullException">If serviceType or callback is null.</exception>
204         public abstract void Unsubscribe(Type serviceType, SubscribeServiceCallback callback);
205
206         /// <summary>
207         ///     This is a helper method that returns the target object for a delegate.
208         ///     If the delegate was created to proxy a generic delegate, this will correctly
209         ///     return the original object, not the proxy.
210         /// </summary>
211         /// <param name="callback">The delegate to get the target for.</param>
212         /// <returns>The object that is the callback target.  This can return null if the callback represents a static object.</returns>
213         /// <exception cref="ArgumentNullException">If callback is null.</exception>
214         protected static object GetTarget(Delegate callback)
215         {
216             if (callback == null) throw FxTrace.Exception.ArgumentNull("callback");
217             ICallbackProxy proxy = callback.Target as ICallbackProxy;
218             if (proxy != null)
219             {
220                 return proxy.OriginalTarget;
221             }
222
223             return callback.Target;
224         }
225
226         /// <summary>
227         ///     This is a helper method that performs a Delegate.Remove, but knows
228         ///     how to unwrap delegates that are proxies to generic callbacks.  Use
229         ///     this in your Unsubscribe implementations.
230         /// </summary>
231         /// <param name="existing">The existing delegate to remove the callback from.</param>
232         /// <param name="toRemove">The callback to remove.</param>
233         /// <returns>A new value to assign to the existing delegate.</returns>
234         protected static Delegate RemoveCallback(Delegate existing, Delegate toRemove)
235         {
236             if (existing == null) return null;
237             if (toRemove == null) return existing;
238
239             ICallbackProxy toRemoveProxy = toRemove.Target as ICallbackProxy;
240             if (toRemoveProxy == null)
241             {
242                 // The item to be removed is a normal delegate.  Just call
243                 // Delegate.Remove
244                 return Delegate.Remove(existing, toRemove);
245             }
246
247             toRemove = toRemoveProxy.OriginalDelegate;
248
249             Delegate[] invocationList = existing.GetInvocationList();
250             bool removedItems = false;
251
252             for (int idx = 0; idx < invocationList.Length; idx++)
253             {
254                 Delegate item = invocationList[idx];
255                 ICallbackProxy itemProxy = item.Target as ICallbackProxy;
256                 if (itemProxy != null)
257                 {
258                     item = itemProxy.OriginalDelegate;
259                 }
260
261                 if (item.Equals(toRemove))
262                 {
263                     invocationList[idx] = null;
264                     removedItems = true;
265                 }
266             }
267
268             if (removedItems)
269             {
270                 // We must create a new delegate containing the 
271                 // invocation list that is is left
272                 existing = null;
273                 foreach (Delegate d in invocationList)
274                 {
275                     if (d != null)
276                     {
277                         if (existing == null)
278                         {
279                             existing = d;
280                         }
281                         else
282                         {
283                             existing = Delegate.Combine(existing, d);
284                         }
285                     }
286                 }
287             }
288
289             return existing;
290         }
291
292         /// <summary>
293         /// Implementation of default IEnumerable.
294         /// </summary>
295         IEnumerator IEnumerable.GetEnumerator()
296         {
297             return GetEnumerator();
298         }
299
300         /// <summary>
301         /// This is a simple proxy that converts a non-generic publish callback to a generic
302         /// one.
303         /// </summary>
304         /// <typeparam name="TServiceType"></typeparam>
305         private class PublishProxy<TServiceType>
306         {
307             private PublishServiceCallback<TServiceType> _genericCallback;
308
309             internal PublishProxy(PublishServiceCallback<TServiceType> callback)
310             {
311                 _genericCallback = callback;
312             }
313
314             internal PublishServiceCallback Callback
315             {
316                 get
317                 {
318                     return new PublishServiceCallback(PublishService);
319                 }
320             }
321
322             private object PublishService(Type serviceType)
323             {
324
325                 if (serviceType == null) throw FxTrace.Exception.ArgumentNull("serviceType");
326
327                 if (serviceType != typeof(TServiceType))
328                 {
329                     // This is an invalid publisher
330                     throw FxTrace.Exception.AsError(new InvalidOperationException(string.Format(
331                         CultureInfo.CurrentCulture,
332                         Resources.Error_IncorrectServiceType,
333                         typeof(ServiceManager).FullName,
334                         typeof(TServiceType).FullName,
335                         serviceType.FullName)));
336                 }
337
338                 object service = _genericCallback();
339
340                 if (service == null)
341                 {
342                     throw FxTrace.Exception.AsError(new InvalidOperationException(
343                         string.Format(
344                         CultureInfo.CurrentCulture,
345                         Resources.Error_NullService,
346                         _genericCallback.Method.DeclaringType.FullName,
347                         serviceType.FullName)));
348                 }
349
350                 if (!serviceType.IsInstanceOfType(service))
351                 {
352                     throw FxTrace.Exception.AsError(new InvalidOperationException(
353                         string.Format(
354                         CultureInfo.CurrentCulture,
355                         Resources.Error_IncorrectServiceType,
356                         _genericCallback.Method.DeclaringType.FullName,
357                         serviceType.FullName,
358                         service.GetType().FullName)));
359                 }
360
361                 return service;
362             }
363         }
364
365         /// <summary>
366         /// This is a simple proxy that converts a non-generic subscribe callback to a generic
367         /// one.
368         /// </summary>
369         /// <typeparam name="TServiceType"></typeparam>
370         private class SubscribeProxy<TServiceType> : ICallbackProxy
371         {
372             private SubscribeServiceCallback<TServiceType> _genericCallback;
373
374             internal SubscribeProxy(SubscribeServiceCallback<TServiceType> callback)
375             {
376                 _genericCallback = callback;
377             }
378
379             internal SubscribeServiceCallback Callback
380             {
381                 get
382                 {
383                     return new SubscribeServiceCallback(SubscribeService);
384                 }
385             }
386
387
388             private void SubscribeService(Type serviceType, object service)
389             {
390
391                 if (serviceType == null) throw FxTrace.Exception.ArgumentNull("serviceType");
392                 if (service == null) throw FxTrace.Exception.ArgumentNull("service");
393
394                 if (!typeof(TServiceType).IsInstanceOfType(service))
395                 {
396                     // This is an invalid subscriber
397                     throw FxTrace.Exception.AsError(new InvalidOperationException(string.Format(
398                         CultureInfo.CurrentCulture,
399                         Resources.Error_IncorrectServiceType,
400                         typeof(TServiceType).FullName,
401                         serviceType.FullName)));
402                 }
403
404                 _genericCallback((TServiceType)service);
405             }
406
407             Delegate ICallbackProxy.OriginalDelegate
408             {
409                 get { return _genericCallback; }
410             }
411
412             object ICallbackProxy.OriginalTarget
413             {
414                 get
415                 {
416                     return _genericCallback.Target;
417                 }
418             }
419         }
420
421         private interface ICallbackProxy
422         {
423             Delegate OriginalDelegate { get; }
424             object OriginalTarget { get; }
425         }
426     }
427 }