namespace System.Activities.Presentation { using System.Activities.Presentation.Internal.Properties; using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Activities.Presentation; /// /// The service manager implements IServiceProvider and provides access /// to services offered by the editing context. /// /// Suppressing FxCop from complaining about our use of naming, since it has been approved [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] public abstract class ServiceManager : IServiceProvider, IEnumerable { /// /// Creates a new ServiceManager. /// protected ServiceManager() { } /// /// Returns true if the service manager contains a service of the given type. /// /// The type of service to check. /// True if a service of type serviceType has been published. /// if serviceType is null. public abstract bool Contains(Type serviceType); /// /// Returns true if the service manager contains a service of the given type. /// /// The type of service to check. /// True if a service of type TServiceType has been published. [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] public bool Contains() { return Contains(typeof(TServiceType)); } /// /// Retrives the requested service. Unlike GetService, GetRequiredService /// throws a NotSupportedException if the service isn’t available. The reason /// we provide this method, and not a normal GetService method is our wish to /// move services to a more reliable contract. /// /// The type of service to retrieve. /// An instance of the service. This never returns null. /// if there is no service of the given type. [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] public TServiceType GetRequiredService() { TServiceType service = GetService(); if (service == null) { throw FxTrace.Exception.AsError(new NotSupportedException( string.Format(CultureInfo.CurrentCulture, Resources.Error_RequiredService, typeof(TServiceType).FullName))); } return service; } /// /// Retrives the requested service. This method returns null if the service could not be located. /// /// The type of service to retrieve. /// An instance of the service, or null if the service has not been published. [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] public TServiceType GetService() { object service = GetService(typeof(TServiceType)); return (TServiceType)service; } /// /// Retrives the requested service. This method returns null if the service could not be located. /// /// The type of service to retrieve. /// An instance of the service, or null if the service has not been published. /// If serviceType is null. public abstract object GetService(Type serviceType); /// /// Retrives an enumerator that can be used to enumerate all of the services that this /// service manager publishes. /// /// An enumeration of published services. public abstract IEnumerator GetEnumerator(); /// /// Calls back on the provided callback when someone has published the requested service. /// If the service was already available, this method invokes the callback immediately. /// /// A generic version of this method is provided for convenience, and calls the non-generic /// method with appropriate casts. /// /// The type of service to subscribe to. /// A callback that will be notified when the service is available. /// If serviceType or callback is null. public abstract void Subscribe(Type serviceType, SubscribeServiceCallback callback); /// /// Calls back on the provided callback when someone has published the requested service. /// If the service was already available, this method invokes the callback immediately. /// /// A generic version of this method is provided for convenience, and calls the non-generic /// method with appropriate casts. /// /// The type of service to subscribe. /// A callback that will be invoked when the service is available. /// If callback is null. [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] public void Subscribe(SubscribeServiceCallback callback) { if (callback == null) throw FxTrace.Exception.ArgumentNull("callback"); // Call the standard Subscribe method and use a generic proxy SubscribeProxy proxy = new SubscribeProxy(callback); Subscribe(typeof(TServiceType), proxy.Callback); } /// /// Publishes the given service type, but does not declare an instance yet. When someone /// requests the service the PublishServiceCallback will be invoked to create the instance. /// The callback is only invoked once and after that the instance it returned is cached. /// /// A generic version of this method is provided for convenience, and calls the non-generic /// method with appropriate casts. /// /// The type of service to publish. /// A callback that will be invoked when an instance of the service is needed. /// If serviceType or callback is null. /// If serviceType has already been published. public abstract void Publish(Type serviceType, PublishServiceCallback callback); /// /// Publishes the given service. Once published, the service instance remains in the /// service manager until the editing context is disposed. /// /// The type of service to publish. /// An instance of the service. /// If serviceType or serviceInstance is null. /// If serviceInstance does not derive from or implement serviceType, or if serviceType has already been published. public abstract void Publish(Type serviceType, object serviceInstance); /// /// Publishes the given service type, but does not declare an instance yet. When someone /// requests the service the PublishServiceCallback will be invoked to create the instance. /// The callback is only invoked once and after that the instance it returned is cached. /// /// The type of service to publish. /// A callback to be invoked when the service is required. /// If callback is null. /// If TServiceType has already been published. [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] public void Publish(PublishServiceCallback callback) { if (callback == null) throw FxTrace.Exception.ArgumentNull("callback"); // Call the standard Subscribe method and use a generic proxy PublishProxy proxy = new PublishProxy(callback); Publish(typeof(TServiceType), proxy.Callback); } /// /// Publishes the given service. Once published, the service instance remains in the /// service manager until the editing context is disposed. /// /// The type of service to publish. /// The instance of the service to publish. /// If TServiceType has already been published. public void Publish(TServiceType serviceInstance) { if (serviceInstance == null) throw FxTrace.Exception.ArgumentNull("serviceInstance"); Publish(typeof(TServiceType), serviceInstance); } /// /// Removes a subscription for the TServiceType. /// /// The type of service to remove the subscription from. /// The callback object to remove from the subscription. /// If callback is null. [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] public void Unsubscribe(SubscribeServiceCallback callback) { if (callback == null) throw FxTrace.Exception.ArgumentNull("callback"); SubscribeProxy proxy = new SubscribeProxy(callback); Unsubscribe(typeof(TServiceType), proxy.Callback); } /// /// Removes a subscription for the serviceType. /// /// The type of service to remove the subscription from. /// The callback object to remove from the subscription. /// If serviceType or callback is null. public abstract void Unsubscribe(Type serviceType, SubscribeServiceCallback callback); /// /// This is a helper method that returns the target object for a delegate. /// If the delegate was created to proxy a generic delegate, this will correctly /// return the original object, not the proxy. /// /// The delegate to get the target for. /// The object that is the callback target. This can return null if the callback represents a static object. /// If callback is null. protected static object GetTarget(Delegate callback) { if (callback == null) throw FxTrace.Exception.ArgumentNull("callback"); ICallbackProxy proxy = callback.Target as ICallbackProxy; if (proxy != null) { return proxy.OriginalTarget; } return callback.Target; } /// /// This is a helper method that performs a Delegate.Remove, but knows /// how to unwrap delegates that are proxies to generic callbacks. Use /// this in your Unsubscribe implementations. /// /// The existing delegate to remove the callback from. /// The callback to remove. /// A new value to assign to the existing delegate. protected static Delegate RemoveCallback(Delegate existing, Delegate toRemove) { if (existing == null) return null; if (toRemove == null) return existing; ICallbackProxy toRemoveProxy = toRemove.Target as ICallbackProxy; if (toRemoveProxy == null) { // The item to be removed is a normal delegate. Just call // Delegate.Remove return Delegate.Remove(existing, toRemove); } toRemove = toRemoveProxy.OriginalDelegate; Delegate[] invocationList = existing.GetInvocationList(); bool removedItems = false; for (int idx = 0; idx < invocationList.Length; idx++) { Delegate item = invocationList[idx]; ICallbackProxy itemProxy = item.Target as ICallbackProxy; if (itemProxy != null) { item = itemProxy.OriginalDelegate; } if (item.Equals(toRemove)) { invocationList[idx] = null; removedItems = true; } } if (removedItems) { // We must create a new delegate containing the // invocation list that is is left existing = null; foreach (Delegate d in invocationList) { if (d != null) { if (existing == null) { existing = d; } else { existing = Delegate.Combine(existing, d); } } } } return existing; } /// /// Implementation of default IEnumerable. /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } /// /// This is a simple proxy that converts a non-generic publish callback to a generic /// one. /// /// private class PublishProxy { private PublishServiceCallback _genericCallback; internal PublishProxy(PublishServiceCallback callback) { _genericCallback = callback; } internal PublishServiceCallback Callback { get { return new PublishServiceCallback(PublishService); } } private object PublishService(Type serviceType) { if (serviceType == null) throw FxTrace.Exception.ArgumentNull("serviceType"); if (serviceType != typeof(TServiceType)) { // This is an invalid publisher throw FxTrace.Exception.AsError(new InvalidOperationException(string.Format( CultureInfo.CurrentCulture, Resources.Error_IncorrectServiceType, typeof(ServiceManager).FullName, typeof(TServiceType).FullName, serviceType.FullName))); } object service = _genericCallback(); if (service == null) { throw FxTrace.Exception.AsError(new InvalidOperationException( string.Format( CultureInfo.CurrentCulture, Resources.Error_NullService, _genericCallback.Method.DeclaringType.FullName, serviceType.FullName))); } if (!serviceType.IsInstanceOfType(service)) { throw FxTrace.Exception.AsError(new InvalidOperationException( string.Format( CultureInfo.CurrentCulture, Resources.Error_IncorrectServiceType, _genericCallback.Method.DeclaringType.FullName, serviceType.FullName, service.GetType().FullName))); } return service; } } /// /// This is a simple proxy that converts a non-generic subscribe callback to a generic /// one. /// /// private class SubscribeProxy : ICallbackProxy { private SubscribeServiceCallback _genericCallback; internal SubscribeProxy(SubscribeServiceCallback callback) { _genericCallback = callback; } internal SubscribeServiceCallback Callback { get { return new SubscribeServiceCallback(SubscribeService); } } private void SubscribeService(Type serviceType, object service) { if (serviceType == null) throw FxTrace.Exception.ArgumentNull("serviceType"); if (service == null) throw FxTrace.Exception.ArgumentNull("service"); if (!typeof(TServiceType).IsInstanceOfType(service)) { // This is an invalid subscriber throw FxTrace.Exception.AsError(new InvalidOperationException(string.Format( CultureInfo.CurrentCulture, Resources.Error_IncorrectServiceType, typeof(TServiceType).FullName, serviceType.FullName))); } _genericCallback((TServiceType)service); } Delegate ICallbackProxy.OriginalDelegate { get { return _genericCallback; } } object ICallbackProxy.OriginalTarget { get { return _genericCallback.Target; } } } private interface ICallbackProxy { Delegate OriginalDelegate { get; } object OriginalTarget { get; } } } }