// Imagine a generic AddInBase (AB<T>), and an AddInAdapter with a
        // constructor taking in AB<Int32>.  If we have IntAddIn : AB<Int32>,
        // then we should be able to hook this up.
        internal bool CanConnectTo(AddInBase addInBase)
        {
            System.Diagnostics.Contracts.Contract.Requires(addInBase != null);

            if (!addInBase.TypeInfo.IsGeneric)
            {
                if (this.Constructors.Contains(addInBase.TypeInfo))
                {
                    return(true);
                }

                // return true if we have a constructor that accepts one of addinBase's ActivatableAs base classes
                if (addInBase._activatableAs != null)
                {
                    foreach (TypeInfo activatableAsTypeInfo in addInBase._activatableAs)
                    {
                        if (this.Constructors.Contains(activatableAsTypeInfo))
                        {
                            return(true);
                        }
                    }
                }
            }
            else
            {
                return(false);
            }
            return(false);
        }
        internal InspectionResults Inspect(PipelineComponentType componentType, string assemblyFileName, string pipelineRootDirectory)
        {
            System.Diagnostics.Contracts.Contract.Requires(assemblyFileName != null);
            System.Diagnostics.Contracts.Contract.Requires(pipelineRootDirectory != null);

            _assemblyFileName = assemblyFileName;
            _pipelineRootDirectory = pipelineRootDirectory;

            // Set up the assembly resolve event.
            _currentComponentType = componentType;
            ResolveEventHandler assemblyResolver = new ResolveEventHandler(ResolveAssembly);
            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += assemblyResolver;

            InspectionResults retval = new InspectionResults();
            retval.Components = new List<PipelineComponent>();
            retval.Warnings = new Collection<String>();
            Type[] publicTypes;
            String assemblyName = null;

            // Need to assert again here because we are in a new appdomain
            FileIOPermission permission = new FileIOPermission(FileIOPermissionAccess.PathDiscovery |
                    FileIOPermissionAccess.Read, _pipelineRootDirectory);
            permission.Assert();

            try
            {
                // We want to load the assembly WITHOUT REGARD OF PUBLISHER POLICY.
                // If the directory structure contains v1.0 of a component and v1.1
                // exists in the GAC and is a security fix to v1.0, we still want to
                // inspect v1.0.  (The reason is we have other parts of the 
                // pipeline that were likely compiled against v1.0, not v1.1, and
                // we do type comparisons by comparing the fully qualified assembly 
                // name.)  LoadFrom unfortunately respects policy.  Assembly's
                // ReflectionOnlyLoad(byte[]) doesn't.  ReflectionOnlyLoadFrom(String)
                // does respect policy if you've set DEVPATH, but only as a bug.
                // We don't think setting DEVPATH is interesting.
                Assembly a = Assembly.ReflectionOnlyLoadFrom(_assemblyFileName);
                publicTypes = a.GetTypes();
                assemblyName = a.FullName;
            }
            catch (FileNotFoundException fnf)
            {
                retval.Warnings.Add(String.Format(CultureInfo.CurrentCulture, Res.AssemblyLoadFileNotFound, fnf.Message, fnf.FileName));
                return retval;
            }
            catch (Exception e)
            {
                retval.Warnings.Add(String.Format(CultureInfo.CurrentCulture, Res.AssemblyLoadThrew, e.GetType().Name, e.Message, _assemblyFileName));
                return retval;
            }
            PipelineComponent component = null;

            String relativeFileName = Utils.MakeRelativePath(_assemblyFileName, _pipelineRootDirectory);
            Type lastType = null;
            try
            {
                // Iterate over public types, looking for the appropriate custom attributes.
                foreach (Type type in publicTypes)
                {
                    component = null;
                    lastType = type;
                    switch (componentType)
                    {
                        case PipelineComponentType.Contract:
                            if (!Utils.HasCustomAttribute(PipelineComponent.ContractAttributeInReflectionLoaderContext, type))
                                continue;

                            component = new ContractComponent(new TypeInfo(type), relativeFileName);
                            break;

                        case PipelineComponentType.AddInAdapter:
                            if (!Utils.HasCustomAttribute(PipelineComponent.AddInAdapterAttributeInReflectionLoaderContext, type))
                                continue;

                            component = new AddInAdapter(new TypeInfo(type), relativeFileName);
                            break;

                        case PipelineComponentType.AddInBase:
                            if (Utils.HasCustomAttribute(PipelineComponent.AddInAttributeInReflectionLoaderContext, type))
                                retval.Warnings.Add(String.Format(CultureInfo.CurrentCulture, Res.AddInInAddInViewFolder, type.Name, _assemblyFileName));

                            if (!Utils.HasCustomAttribute(PipelineComponent.AddInBaseAttributeInReflectionLoaderContext, type))
                                continue;

                            TypeInfo[] activatableAs = null;
                            CustomAttributeData cad = Utils.GetCustomAttributeData(PipelineComponent.AddInBaseAttributeInReflectionLoaderContext, type);
                            foreach(CustomAttributeNamedArgument cana in cad.NamedArguments)
                            {
                                if (cana.MemberInfo.Name == "ActivatableAs")
                                {
                                    CustomAttributeTypedArgument arg = cana.TypedValue;
                                    ReadOnlyCollection<CustomAttributeTypedArgument> types = (ReadOnlyCollection<CustomAttributeTypedArgument>)arg.Value;
                                    activatableAs = new TypeInfo[types.Count];
                                    int i = 0;
                                    foreach (CustomAttributeTypedArgument subArg in types)
                                    {
                                        activatableAs[i++] = new TypeInfo((Type)subArg.Value);
                                    }
                                }
                            }

                            component = new AddInBase(new TypeInfo(type), activatableAs, relativeFileName, assemblyName);

                            break;

                        default:
                            System.Diagnostics.Contracts.Contract.Assert(false, "Fell through switch - unrecognized componentType in InspectionWorker.Inspect");
                            break;
                    }  // switch

                    // If we found a component, make sure it satisfies all of its constraints, and give our
                    // PipelineComponents a chance to initialize state.
                    if (component != null)
                    {
                        if (component.Validate(type, retval.Warnings))
                            retval.Components.Add(component);
                    }
                } // foreach type in the assembly
            } // try
            catch (FileNotFoundException fnf)
            {
                retval.Warnings.Add(String.Format(CultureInfo.CurrentCulture, Res.AssemblyLoadFileNotFound, fnf.Message, fnf.FileName));
                return retval;
            }
            catch (NotImplementedException)
            {
                retval.Warnings.Add(String.Format(CultureInfo.CurrentCulture, Res.NotImplementedFeatureBadCtorParamOrAssembly, 
                    _assemblyFileName, (lastType == null) ? "" : lastType.FullName));
                return retval;
            }
            catch (Exception e)
            {
                retval.Warnings.Add(String.Format(CultureInfo.CurrentCulture, Res.InspectingAssemblyThrew, e.GetType().Name, e.Message, _assemblyFileName));
                return retval;
            }

            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= assemblyResolver;

            if (retval.Components.Count == 0 && _currentComponentType != PipelineComponentType.AddIn && _currentComponentType != PipelineComponentType.AddInBase)
            {
                retval.Warnings.Add(String.Format(CultureInfo.CurrentCulture, Res.NoAddInModelPartsFound, componentType, _assemblyFileName));
            }
#if ADDIN_VERBOSE_WARNINGS
            foreach (PipelineComponent c in retval.Components)
                retval.Warnings.Add(String.Format(CultureInfo.CurrentCulture, "Found a {0}.  Name: {1}  Assembly: {2}", componentType, c.SimpleName, c.AssemblySimpleName));
#endif
            return retval;
        }
        // Imagine a generic AddInBase (AB<T>), and an AddInAdapter with a 
        // constructor taking in AB<Int32>.  If we have IntAddIn : AB<Int32>, 
        // then we should be able to hook this up.
        internal bool CanConnectTo(AddInBase addInBase)
        {
            System.Diagnostics.Contracts.Contract.Requires(addInBase != null);

            if (!addInBase.TypeInfo.IsGeneric)
            {
                if (this.Constructors.Contains(addInBase.TypeInfo))
                    return true;
                
                // return true if we have a constructor that accepts one of addinBase's ActivatableAs base classes
                if (addInBase._activatableAs != null)
                {
                    foreach (TypeInfo activatableAsTypeInfo in addInBase._activatableAs)
                    {
                        if (this.Constructors.Contains(activatableAsTypeInfo))
                            return true;
                    }
                }
            }
            else
            {
                return false;
            }
            return false;
        }