File: cdf\src\NetFx40\Tools\Microsoft.Tools.Common\Microsoft\VisualStudio\Activities\WorkflowDesignerExtensionManager.cs
Project: ndp\System.Data.csproj (System.Data)
// <copyright>
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
 
namespace Microsoft.VisualStudio.Activities
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel.Composition.Hosting;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.IO;
    using System.Reflection;
 
    internal abstract class WorkflowDesignerExtensionManager
    {
        private static MethodInfo internalGetExtensionMethodInfo;
        private static MethodInfo internalGetExtensionsMethodInfo;
        private IExceptionLogger logger;
 
        internal WorkflowDesignerExtensionManager(IExceptionLogger logger)
        {
            Debug.Assert(logger != null, "Logger should not be null and is ensured by caller.");
            this.logger = logger;
        }
 
        protected abstract string ExtensionsDirectory
        {
            get;
        }
 
        private static MethodInfo InternalGetExtensionMethodInfo
        {
            get
            {
                if (WorkflowDesignerExtensionManager.internalGetExtensionMethodInfo == null)
                {
                    // This is a justified private reflection since it access itself only.
                    internalGetExtensionMethodInfo = typeof(WorkflowDesignerExtensionManager).GetMethod("InternalGetExtension", BindingFlags.Instance | BindingFlags.NonPublic);
                }
 
                return WorkflowDesignerExtensionManager.internalGetExtensionMethodInfo;
            }
        }
 
        private static MethodInfo InternalGetExtensionsMethodInfo
        {
            get
            {
                if (WorkflowDesignerExtensionManager.internalGetExtensionsMethodInfo == null)
                {
                    // This is a justified private reflection since it access itself only.
                    internalGetExtensionsMethodInfo = typeof(WorkflowDesignerExtensionManager).GetMethod("InternalGetExtensions", BindingFlags.Instance | BindingFlags.NonPublic);
                }
 
                return WorkflowDesignerExtensionManager.internalGetExtensionsMethodInfo;
            }
        }
 
        internal static string GetExtensionsDirectory(string relativePathToExtensionsDirectory)
        {
            string assemblyLocation = Assembly.GetExecutingAssembly().Location;
            string assemblyDirectory = Path.GetDirectoryName(assemblyLocation);
            return Path.Combine(assemblyDirectory, relativePathToExtensionsDirectory);
        }
 
        internal object GetExtension(string extensionTypeAssemblyQualifiedName)
        {
            VSDesignerPerfEventProvider perfEventProvider = new VSDesignerPerfEventProvider();
            perfEventProvider.WriteEvent(VSDesignerPerfEvents.WorkflowDesignerExtensionManagerGetExtensionStart);
            try
            {
                using (ExtensionAssemblyResolvingScope scope = new ExtensionAssemblyResolvingScope(this.logger, this.ExtensionsDirectory))
                {
                    Type hostServiceType = Type.GetType(extensionTypeAssemblyQualifiedName);
                    if (hostServiceType == null)
                    {
                        return null;
                    }
 
                    return this.GetExtension(hostServiceType);
                }
            }
            finally
            {
                perfEventProvider.WriteEvent(VSDesignerPerfEvents.WorkflowDesignerExtensionManagerGetExtensionEnd);
            }
        }
 
        internal T GetExtension<T>()
        {
            return this.InternalGetExtension<T>();
        }
 
        internal IEnumerable<object> GetExtensions(string extensionTypeAssemblyQualifiedName)
        {
            using (ExtensionAssemblyResolvingScope scope = new ExtensionAssemblyResolvingScope(this.logger, this.ExtensionsDirectory))
            {
                Type hostServiceType = Type.GetType(extensionTypeAssemblyQualifiedName);
                if (hostServiceType == null)
                {
                    return null;
                }
 
                return this.GetExtensions(hostServiceType);
            }
        }
 
        internal IEnumerable<T> GetExtensions<T>()
        {
            return this.InternalGetExtensions<T>();
        }
 
        protected abstract void PrepareContext(CompositionContainer container);
 
        private CompositionContainer PrepareCompositionContainer()
        {
            CompositionContainer container = new CompositionContainer(new DirectoryCatalog(this.ExtensionsDirectory));
            this.PrepareContext(container);
            return container;
        }
 
        private object GetExtension(Type hostServiceType)
        {
            return WorkflowDesignerExtensionManager.InternalGetExtensionMethodInfo.MakeGenericMethod(hostServiceType).Invoke(this, null);
        }
 
        private IEnumerable<object> GetExtensions(Type hostServiceType)
        {
            return WorkflowDesignerExtensionManager.InternalGetExtensionsMethodInfo.MakeGenericMethod(hostServiceType).Invoke(this, null) as IEnumerable<object>;
        }
 
        private T InternalGetExtension<T>()
        {
            if (Directory.Exists(this.ExtensionsDirectory))
            {
                return this.PrepareCompositionContainer().GetExportedValueOrDefault<T>();
            }
            else
            {
                return default(T);
            }
        }
 
        private IEnumerable<T> InternalGetExtensions<T>()
        {
            if (Directory.Exists(this.ExtensionsDirectory))
            {
                return this.PrepareCompositionContainer().GetExportedValues<T>();
            }
            else
            {
                // Make it convenient for the caller so that they don't have to do NULL check.
                return new Collection<T>();
            }
        }
 
        private class ExtensionAssemblyResolvingScope : IDisposable
        {
            private string extensionsDirectory;
            private ResolveEventHandler extensionAssemblyResolver;
            private IExceptionLogger logger;
 
            public ExtensionAssemblyResolvingScope(IExceptionLogger logger, string extensionsDirectory)
            {
                Debug.Assert(logger != null, "Logger should not be null and is ensured by caller.");
                Debug.Assert(extensionsDirectory != null, "extensionsDirectory should not be null and is ensured by caller.");
                this.logger = logger;
                this.extensionsDirectory = extensionsDirectory;
                AppDomain.CurrentDomain.AssemblyResolve += this.ExtensionAssemblyResolver;
            }
 
            private ResolveEventHandler ExtensionAssemblyResolver
            {
                get
                {
                    if (this.extensionAssemblyResolver == null)
                    {
                        this.extensionAssemblyResolver = new ResolveEventHandler(this.ResolveExtensionAssembly);
                    }
 
                    return this.extensionAssemblyResolver;
                }
            }
 
            public void Dispose()
            {
                AppDomain.CurrentDomain.AssemblyResolve -= this.ExtensionAssemblyResolver;
            }
 
            [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", Justification = "We need to call LoadAssembly here")]
            private Assembly ResolveExtensionAssembly(object sender, ResolveEventArgs args)
            {
                // Find the assembly in extensions path
                string assemblyName = args.Name.Substring(0, args.Name.IndexOf(",", StringComparison.Ordinal));
                string assemblyFullPath = Path.Combine(this.extensionsDirectory, assemblyName + ".dll");
                try
                {
                    return Assembly.LoadFrom(assemblyFullPath);
                }
                catch (Exception ex)
                {
                    if (ExceptionUtility.IsFatal(ex))
                    {
                        throw;
                    }
 
                    this.logger.LogException(ex);
                    return null;
                }
            }
        }
    }
}