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; }
}
}
}