using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
namespace PalGain.Core
{
///
/// A class that finds types needed by Nop by looping assemblies in the
/// currently executing AppDomain. Only assemblies whose names matches
/// certain patterns are investigated and an optional list of assemblies
/// referenced by are always investigated.
///
public class AppDomainTypeFinder : ITypeFinder
{
#region Fields
private bool ignoreReflectionErrors = true;
private bool loadAppDomainAssemblies = true;
private string assemblySkipLoadingPattern = "^System|^mscorlib|^Microsoft|^AjaxControlToolkit|^Antlr3|^Autofac|^AutoMapper|^Castle|^ComponentArt|^CppCodeProvider|^DotNetOpenAuth|^EntityFramework|^EPPlus|^FluentValidation|^ImageResizer|^itextsharp|^log4net|^MaxMind|^MbUnit|^MiniProfiler|^Mono.Math|^MvcContrib|^Newtonsoft|^NHibernate|^nunit|^Org.Mentalis|^PerlRegex|^QuickGraph|^Recaptcha|^Remotion|^RestSharp|^Rhino|^Telerik|^Iesi|^TestDriven|^TestFu|^UserAgentStringLibrary|^VJSharpCodeProvider|^WebActivator|^WebDev|^WebGrease";
private string assemblyRestrictToLoadingPattern = ".*";
private IList assemblyNames = new List();
#endregion
#region Properties
/// The app domain to look for types in.
public virtual AppDomain App
{
get { return AppDomain.CurrentDomain; }
}
/// Gets or sets wether Nop should iterate assemblies in the app domain when loading Nop types. Loading patterns are applied when loading these assemblies.
public bool LoadAppDomainAssemblies
{
get { return loadAppDomainAssemblies; }
set { loadAppDomainAssemblies = value; }
}
/// Gets or sets assemblies loaded a startup in addition to those loaded in the AppDomain.
public IList AssemblyNames
{
get { return assemblyNames; }
set { assemblyNames = value; }
}
/// Gets the pattern for dlls that we know don't need to be investigated.
public string AssemblySkipLoadingPattern
{
get { return assemblySkipLoadingPattern; }
set { assemblySkipLoadingPattern = value; }
}
/// Gets or sets the pattern for dll that will be investigated. For ease of use this defaults to match all but to increase performance you might want to configure a pattern that includes assemblies and your own.
/// If you change this so that Nop assemblies arn't investigated (e.g. by not including something like "^Nop|..." you may break core functionality.
public string AssemblyRestrictToLoadingPattern
{
get { return assemblyRestrictToLoadingPattern; }
set { assemblyRestrictToLoadingPattern = value; }
}
#endregion
#region Methods
public IEnumerable FindClassesOfType(bool onlyConcreteClasses = true)
{
return FindClassesOfType(typeof(T), onlyConcreteClasses);
}
public IEnumerable FindClassesOfType(Type assignTypeFrom, bool onlyConcreteClasses = true)
{
return FindClassesOfType(assignTypeFrom, GetAssemblies(), onlyConcreteClasses);
}
public IEnumerable FindClassesOfType(IEnumerable assemblies, bool onlyConcreteClasses = true)
{
return FindClassesOfType(typeof (T), assemblies, onlyConcreteClasses);
}
public IEnumerable FindClassesOfType(Type assignTypeFrom, IEnumerable assemblies, bool onlyConcreteClasses = true)
{
var result = new List();
try
{
foreach (var a in assemblies)
{
Type[] types = null;
try
{
types = a.GetTypes();
}
catch(Exception ex)
{
if (ex is System.Reflection.ReflectionTypeLoadException)
{
var typeLoadException = ex as ReflectionTypeLoadException;
var loaderExceptions = typeLoadException.LoaderExceptions;
}
//Entity Framework 6 doesn't allow getting types (throws an exception)
if (!ignoreReflectionErrors)
{
throw;
}
}
if (types != null)
{
foreach (var t in types)
{
if (assignTypeFrom.IsAssignableFrom(t) || (assignTypeFrom.IsGenericTypeDefinition && DoesTypeImplementOpenGeneric(t, assignTypeFrom)))
{
if (!t.IsInterface)
{
if (onlyConcreteClasses)
{
if (t.IsClass && !t.IsAbstract)
{
result.Add(t);
}
}
else
{
result.Add(t);
}
}
}
}
}
}
}
catch (ReflectionTypeLoadException ex)
{
var msg = string.Empty;
foreach (var e in ex.LoaderExceptions)
msg += e.Message + Environment.NewLine;
var fail = new Exception(msg, ex);
Debug.WriteLine(fail.Message, fail);
throw fail;
}
return result;
}
/// Gets the assemblies related to the current implementation.
/// A list of assemblies that should be loaded by the Nop factory.
public virtual IList GetAssemblies()
{
var addedAssemblyNames = new List();
var assemblies = new List();
if (LoadAppDomainAssemblies)
AddAssembliesInAppDomain(addedAssemblyNames, assemblies);
AddConfiguredAssemblies(addedAssemblyNames, assemblies);
return assemblies;
}
#endregion
#region Utilities
///
/// Iterates all assemblies in the AppDomain and if it's name matches the configured patterns add it to our list.
///
///
///
private void AddAssembliesInAppDomain(List addedAssemblyNames, List assemblies)
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (Matches(assembly.FullName))
{
if (!addedAssemblyNames.Contains(assembly.FullName))
{
assemblies.Add(assembly);
addedAssemblyNames.Add(assembly.FullName);
}
}
}
}
///
/// Adds specificly configured assemblies.
///
///
///
protected virtual void AddConfiguredAssemblies(List addedAssemblyNames, List assemblies)
{
foreach (string assemblyName in AssemblyNames)
{
Assembly assembly = Assembly.Load(assemblyName);
if (!addedAssemblyNames.Contains(assembly.FullName))
{
assemblies.Add(assembly);
addedAssemblyNames.Add(assembly.FullName);
}
}
}
///
/// Check if a dll is one of the shipped dlls that we know don't need to be investigated.
///
///
/// The name of the assembly to check.
///
///
/// True if the assembly should be loaded into Nop.
///
public virtual bool Matches(string assemblyFullName)
{
return !Matches(assemblyFullName, AssemblySkipLoadingPattern)
&& Matches(assemblyFullName, AssemblyRestrictToLoadingPattern);
}
///
/// Check if a dll is one of the shipped dlls that we know don't need to be investigated.
///
///
/// The assembly name to match.
///
///
/// The regular expression pattern to match against the assembly name.
///
///
/// True if the pattern matches the assembly name.
///
protected virtual bool Matches(string assemblyFullName, string pattern)
{
return Regex.IsMatch(assemblyFullName, pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
}
///
/// Makes sure matching assemblies in the supplied folder are loaded in the app domain.
///
///
/// The physical path to a directory containing dlls to load in the app domain.
///
protected virtual void LoadMatchingAssemblies(string directoryPath)
{
var loadedAssemblyNames = new List();
foreach (Assembly a in GetAssemblies())
{
loadedAssemblyNames.Add(a.FullName);
}
if (!Directory.Exists(directoryPath))
{
return;
}
foreach (string dllPath in Directory.GetFiles(directoryPath, "*.dll"))
{
try
{
var an = AssemblyName.GetAssemblyName(dllPath);
if (Matches(an.FullName) && !loadedAssemblyNames.Contains(an.FullName))
{
App.Load(an);
}
//old loading stuff
//Assembly a = Assembly.ReflectionOnlyLoadFrom(dllPath);
//if (Matches(a.FullName) && !loadedAssemblyNames.Contains(a.FullName))
//{
// App.Load(a.FullName);
//}
}
catch (BadImageFormatException ex)
{
Trace.TraceError(ex.ToString());
}
}
}
///
/// Does type implement generic?
///
///
///
///
protected virtual bool DoesTypeImplementOpenGeneric(Type type, Type openGeneric)
{
try
{
var genericTypeDefinition = openGeneric.GetGenericTypeDefinition();
foreach (var implementedInterface in type.FindInterfaces((objType, objCriteria) => true, null))
{
if (!implementedInterface.IsGenericType)
continue;
var isMatch = genericTypeDefinition.IsAssignableFrom(implementedInterface.GetGenericTypeDefinition());
return isMatch;
}
return false;
}catch
{
return false;
}
}
#endregion
}
}