1 namespace System.Activities.Presentation
4 using System.Activities.Presentation.Internal.Properties;
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;
14 /// The service manager implements IServiceProvider and provides access
15 /// to services offered by the editing context.
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>
23 /// Creates a new ServiceManager.
25 protected ServiceManager() { }
28 /// Returns true if the service manager contains a service of the given type.
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);
36 /// Returns true if the service manager contains a service of the given type.
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>()
43 return Contains(typeof(TServiceType));
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.
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>()
59 TServiceType service = GetService<TServiceType>();
62 throw FxTrace.Exception.AsError(new NotSupportedException(
63 string.Format(CultureInfo.CurrentCulture,
64 Resources.Error_RequiredService, typeof(TServiceType).FullName)));
70 /// Retrives the requested service. This method returns null if the service could not be located.
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>()
77 object service = GetService(typeof(TServiceType));
78 return (TServiceType)service;
82 /// Retrives the requested service. This method returns null if the service could not be located.
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);
90 /// Retrives an enumerator that can be used to enumerate all of the services that this
91 /// service manager publishes.
93 /// <returns>An enumeration of published services.</returns>
94 public abstract IEnumerator<Type> GetEnumerator();
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.
100 /// A generic version of this method is provided for convenience, and calls the non-generic
101 /// method with appropriate casts.
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);
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.
112 /// A generic version of this method is provided for convenience, and calls the non-generic
113 /// method with appropriate casts.
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)
121 if (callback == null) throw FxTrace.Exception.ArgumentNull("callback");
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);
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.
133 /// A generic version of this method is provided for convenience, and calls the non-generic
134 /// method with appropriate casts.
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);
143 /// Publishes the given service. Once published, the service instance remains in the
144 /// service manager until the editing context is disposed.
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);
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.
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)
164 if (callback == null) throw FxTrace.Exception.ArgumentNull("callback");
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);
172 /// Publishes the given service. Once published, the service instance remains in the
173 /// service manager until the editing context is disposed.
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)
180 if (serviceInstance == null) throw FxTrace.Exception.ArgumentNull("serviceInstance");
181 Publish(typeof(TServiceType), serviceInstance);
185 /// Removes a subscription for the TServiceType.
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)
193 if (callback == null) throw FxTrace.Exception.ArgumentNull("callback");
194 SubscribeProxy<TServiceType> proxy = new SubscribeProxy<TServiceType>(callback);
195 Unsubscribe(typeof(TServiceType), proxy.Callback);
199 /// Removes a subscription for the serviceType.
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);
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.
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)
216 if (callback == null) throw FxTrace.Exception.ArgumentNull("callback");
217 ICallbackProxy proxy = callback.Target as ICallbackProxy;
220 return proxy.OriginalTarget;
223 return callback.Target;
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.
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)
236 if (existing == null) return null;
237 if (toRemove == null) return existing;
239 ICallbackProxy toRemoveProxy = toRemove.Target as ICallbackProxy;
240 if (toRemoveProxy == null)
242 // The item to be removed is a normal delegate. Just call
244 return Delegate.Remove(existing, toRemove);
247 toRemove = toRemoveProxy.OriginalDelegate;
249 Delegate[] invocationList = existing.GetInvocationList();
250 bool removedItems = false;
252 for (int idx = 0; idx < invocationList.Length; idx++)
254 Delegate item = invocationList[idx];
255 ICallbackProxy itemProxy = item.Target as ICallbackProxy;
256 if (itemProxy != null)
258 item = itemProxy.OriginalDelegate;
261 if (item.Equals(toRemove))
263 invocationList[idx] = null;
270 // We must create a new delegate containing the
271 // invocation list that is is left
273 foreach (Delegate d in invocationList)
277 if (existing == null)
283 existing = Delegate.Combine(existing, d);
293 /// Implementation of default IEnumerable.
295 IEnumerator IEnumerable.GetEnumerator()
297 return GetEnumerator();
301 /// This is a simple proxy that converts a non-generic publish callback to a generic
304 /// <typeparam name="TServiceType"></typeparam>
305 private class PublishProxy<TServiceType>
307 private PublishServiceCallback<TServiceType> _genericCallback;
309 internal PublishProxy(PublishServiceCallback<TServiceType> callback)
311 _genericCallback = callback;
314 internal PublishServiceCallback Callback
318 return new PublishServiceCallback(PublishService);
322 private object PublishService(Type serviceType)
325 if (serviceType == null) throw FxTrace.Exception.ArgumentNull("serviceType");
327 if (serviceType != typeof(TServiceType))
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)));
338 object service = _genericCallback();
342 throw FxTrace.Exception.AsError(new InvalidOperationException(
344 CultureInfo.CurrentCulture,
345 Resources.Error_NullService,
346 _genericCallback.Method.DeclaringType.FullName,
347 serviceType.FullName)));
350 if (!serviceType.IsInstanceOfType(service))
352 throw FxTrace.Exception.AsError(new InvalidOperationException(
354 CultureInfo.CurrentCulture,
355 Resources.Error_IncorrectServiceType,
356 _genericCallback.Method.DeclaringType.FullName,
357 serviceType.FullName,
358 service.GetType().FullName)));
366 /// This is a simple proxy that converts a non-generic subscribe callback to a generic
369 /// <typeparam name="TServiceType"></typeparam>
370 private class SubscribeProxy<TServiceType> : ICallbackProxy
372 private SubscribeServiceCallback<TServiceType> _genericCallback;
374 internal SubscribeProxy(SubscribeServiceCallback<TServiceType> callback)
376 _genericCallback = callback;
379 internal SubscribeServiceCallback Callback
383 return new SubscribeServiceCallback(SubscribeService);
388 private void SubscribeService(Type serviceType, object service)
391 if (serviceType == null) throw FxTrace.Exception.ArgumentNull("serviceType");
392 if (service == null) throw FxTrace.Exception.ArgumentNull("service");
394 if (!typeof(TServiceType).IsInstanceOfType(service))
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)));
404 _genericCallback((TServiceType)service);
407 Delegate ICallbackProxy.OriginalDelegate
409 get { return _genericCallback; }
412 object ICallbackProxy.OriginalTarget
416 return _genericCallback.Target;
421 private interface ICallbackProxy
423 Delegate OriginalDelegate { get; }
424 object OriginalTarget { get; }