diff --git a/src/Umbraco.Core/ApplicationContext.cs b/src/Umbraco.Core/ApplicationContext.cs index 6d4892e911..bd64cb58b5 100644 --- a/src/Umbraco.Core/ApplicationContext.cs +++ b/src/Umbraco.Core/ApplicationContext.cs @@ -1,10 +1,12 @@ using System; -using System.Collections.Generic; -using System.Linq; +using System.Configuration; +using System.Diagnostics; + namespace Umbraco.Core -{ - /// +{ + + /// /// the Umbraco Application context /// /// @@ -59,9 +61,65 @@ namespace Umbraco.Core public bool IsConfigured { // fixme - let's do this for the time being - get { return umbraco.GlobalSettings.Configured; } + get + { + return Configured; + } } + //TODO: This is temporary as we cannot reference umbraco.businesslogic as this will give a circular reference + // CURRENT UMBRACO VERSION ID + private const string _currentVersion = "4.8.0"; + private string CurrentVersion + { + get + { + // change this to be hardcoded in the binary + return _currentVersion; + } + } + private bool Configured + { + get + { + try + { + string configStatus = ConfigurationStatus; + string currentVersion = CurrentVersion; + + + if (currentVersion != configStatus) + { + //Log.Add(LogTypes.Debug, User.GetUser(0), -1, + // "CurrentVersion different from configStatus: '" + currentVersion + "','" + configStatus + + // "'"); + } + + + return (configStatus == currentVersion); + } + catch + { + return false; + } + } + } + private string ConfigurationStatus + { + get + { + try + { + return ConfigurationManager.AppSettings["umbracoConfigurationStatus"]; + } + catch + { + return String.Empty; + } + } + } + + private void AssertIsReady() { if (!this.IsReady) diff --git a/src/Umbraco.Core/ExpressionHelper.cs b/src/Umbraco.Core/ExpressionHelper.cs new file mode 100644 index 0000000000..1293bd47fc --- /dev/null +++ b/src/Umbraco.Core/ExpressionHelper.cs @@ -0,0 +1,306 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace Umbraco.Core +{ + /// + /// A set of helper methods for dealing with expressions + /// + /// + internal static class ExpressionHelper + { + private static readonly ConcurrentDictionary PropertyInfoCache = new ConcurrentDictionary(); + + /// + /// Gets a object from an expression. + /// + /// The type of the source. + /// The type of the property. + /// The source. + /// The property lambda. + /// + /// + public static PropertyInfo GetPropertyInfo(this TSource source, Expression> propertyLambda) + { + return GetPropertyInfo(propertyLambda); + } + + /// + /// Gets a object from an expression. + /// + /// The type of the source. + /// The type of the property. + /// The property lambda. + /// + /// + public static PropertyInfo GetPropertyInfo(Expression> propertyLambda) + { + return PropertyInfoCache.GetOrAdd( + new LambdaExpressionCacheKey(propertyLambda), + x => + { + var type = typeof(TSource); + + var member = propertyLambda.Body as MemberExpression; + if (member == null) + { + if (propertyLambda.Body.GetType().Name == "UnaryExpression") + { + // The expression might be for some boxing, e.g. representing a value type like HiveId as an object + // in which case the expression will be Convert(x.MyProperty) + var unary = propertyLambda.Body as UnaryExpression; + if (unary != null) + { + var boxedMember = unary.Operand as MemberExpression; + if (boxedMember == null) + throw new ArgumentException("The type of property could not be infered, try specifying the type parameters explicitly. This can happen if you have tried to access PropertyInfo where the property's return type is a value type, but the expression is trying to convert it to an object"); + else member = boxedMember; + } + } + else throw new ArgumentException(string.Format("Expression '{0}' refers to a method, not a property.", propertyLambda)); + } + + + var propInfo = member.Member as PropertyInfo; + if (propInfo == null) + throw new ArgumentException(string.Format( + "Expression '{0}' refers to a field, not a property.", + propertyLambda)); + + if (type != propInfo.ReflectedType && + !type.IsSubclassOf(propInfo.ReflectedType)) + throw new ArgumentException(string.Format( + "Expresion '{0}' refers to a property that is not from type {1}.", + propertyLambda, + type)); + + return propInfo; + }); + } + + public static IDictionary GetMethodParams(Expression> fromExpression) + { + if (fromExpression == null) return null; + var body = fromExpression.Body as MethodCallExpression; + if (body == null) + return new Dictionary(); + + var rVal = new Dictionary(); + var parameters = body.Method.GetParameters().Select(x => x.Name).ToArray(); + var i = 0; + foreach (var argument in body.Arguments) + { + var lambda = Expression.Lambda(argument, fromExpression.Parameters); + var d = lambda.Compile(); + var value = d.DynamicInvoke(new object[1]); + rVal.Add(parameters[i], value); + i++; + } + return rVal; + } + + /// + /// Gets a from an provided it refers to a method call. + /// + /// + /// From expression. + /// The or null if is null or cannot be converted to . + /// + public static MethodInfo GetMethodInfo(Expression> fromExpression) + { + if (fromExpression == null) return null; + var body = fromExpression.Body as MethodCallExpression; + return body != null ? body.Method : null; + } + + /// + /// Gets the method info. + /// + /// The return type of the method. + /// From expression. + /// + public static MethodInfo GetMethodInfo(Expression> fromExpression) + { + if (fromExpression == null) return null; + var body = fromExpression.Body as MethodCallExpression; + return body != null ? body.Method : null; + } + + /// + /// Gets the method info. + /// + /// The type of the 1. + /// The type of the 2. + /// From expression. + /// + public static MethodInfo GetMethodInfo(Expression> fromExpression) + { + if (fromExpression == null) return null; + var body = fromExpression.Body as MethodCallExpression; + return body != null ? body.Method : null; + } + + /// + /// Gets a from an provided it refers to a method call. + /// + /// The expression. + /// The or null if cannot be converted to . + /// + public static MethodInfo GetMethod(Expression expression) + { + if (expression == null) return null; + return IsMethod(expression) ? (((MethodCallExpression)expression).Method) : null; + } + + /// + /// Gets a from an provided it refers to member access. + /// + /// + /// The type of the return. + /// From expression. + /// The or null if cannot be converted to . + /// + public static MemberInfo GetMemberInfo(Expression> fromExpression) + { + if (fromExpression == null) return null; + var body = fromExpression.Body as MemberExpression; + return body != null ? body.Member : null; + } + + /// + /// Determines whether the MethodInfo is the same based on signature, not based on the equality operator or HashCode. + /// + /// The left. + /// The right. + /// + /// true if [is method signature equal to] [the specified left]; otherwise, false. + /// + /// + /// This is useful for comparing Expression methods that may contain different generic types + /// + public static bool IsMethodSignatureEqualTo(this MethodInfo left, MethodInfo right) + { + if (left.Equals(right)) + return true; + if (left.DeclaringType != right.DeclaringType) + return false; + if (left.Name != right.Name) + return false; + var leftParams = left.GetParameters(); + var rightParams = right.GetParameters(); + if (leftParams.Length != rightParams.Length) + return false; + for (int i = 0; i < leftParams.Length; i++) + { + //if they are delegate parameters, then assume they match as they could be anything + if (typeof(Delegate).IsAssignableFrom(leftParams[i].ParameterType) && typeof(Delegate).IsAssignableFrom(rightParams[i].ParameterType)) + continue; + //if they are not delegates, then compare the types + if (leftParams[i].ParameterType != rightParams[i].ParameterType) + return false; + } + if (left.ReturnType != right.ReturnType) + return false; + return true; + } + + /// + /// Gets a from an provided it refers to member access. + /// + /// The expression. + /// + /// + public static MemberInfo GetMember(Expression expression) + { + if (expression == null) return null; + return IsMember(expression) ? (((MemberExpression)expression).Member) : null; + } + + /// + /// Gets a from a + /// + /// From method group. + /// + /// + public static MethodInfo GetStaticMethodInfo(Delegate fromMethodGroup) + { + if (fromMethodGroup == null) throw new ArgumentNullException("fromMethodGroup"); + + + return fromMethodGroup.Method; + } + + ///// + ///// Formats an unhandled item for representing the expression as a string. + ///// + ///// + ///// The unhandled item. + ///// + ///// + //public static string FormatUnhandledItem(T unhandledItem) where T : class + //{ + // if (unhandledItem == null) throw new ArgumentNullException("unhandledItem"); + + + // var itemAsExpression = unhandledItem as Expression; + // return itemAsExpression != null + // ? FormattingExpressionTreeVisitor.Format(itemAsExpression) + // : unhandledItem.ToString(); + //} + + /// + /// Determines whether the specified expression is a method. + /// + /// The expression. + /// true if the specified expression is method; otherwise, false. + /// + public static bool IsMethod(Expression expression) + { + return expression != null && typeof(MethodCallExpression).IsAssignableFrom(expression.GetType()); + } + + + + + + /// + /// Determines whether the specified expression is a member. + /// + /// The expression. + /// true if the specified expression is member; otherwise, false. + /// + public static bool IsMember(Expression expression) + { + return expression != null && typeof(MemberExpression).IsAssignableFrom(expression.GetType()); + } + + /// + /// Determines whether the specified expression is a constant. + /// + /// The expression. + /// true if the specified expression is constant; otherwise, false. + /// + public static bool IsConstant(Expression expression) + { + return expression != null && typeof(ConstantExpression).IsAssignableFrom(expression.GetType()); + } + + /// + /// Gets the first value from the supplied arguments of an expression, for those arguments that can be cast to . + /// + /// The arguments. + /// + /// + public static object GetFirstValueFromArguments(IEnumerable arguments) + { + if (arguments == null) return false; + return + arguments.Where(x => typeof(ConstantExpression).IsAssignableFrom(x.GetType())).Cast + ().Select(x => x.Value).DefaultIfEmpty(null).FirstOrDefault(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/LambdaExpressionCacheKey.cs b/src/Umbraco.Core/LambdaExpressionCacheKey.cs new file mode 100644 index 0000000000..9542b041c5 --- /dev/null +++ b/src/Umbraco.Core/LambdaExpressionCacheKey.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace Umbraco.Core +{ + /// + /// Represents a simple in a form which is suitable for using as a dictionary key + /// by exposing the return type, argument types and expression string form in a single concatenated string. + /// + internal struct LambdaExpressionCacheKey + { + public LambdaExpressionCacheKey(string returnType, string expression, params string[] argTypes) + { + ReturnType = returnType; + ExpressionAsString = expression; + ArgTypes = new HashSet(argTypes); + _toString = null; + } + + public LambdaExpressionCacheKey(LambdaExpression obj) + { + ReturnType = obj.ReturnType.FullName; + ExpressionAsString = obj.ToString(); + ArgTypes = new HashSet(obj.Parameters.Select(x => x.Type.FullName)); + _toString = null; + } + + /// + /// The argument type names of the + /// + public readonly HashSet ArgTypes; + + /// + /// The return type of the + /// + public readonly string ReturnType; + + /// + /// The original string representation of the + /// + public readonly string ExpressionAsString; + + private string _toString; + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return _toString ?? (_toString = String.Concat(String.Join("|", ArgTypes), ",", ReturnType, ",", ExpressionAsString)); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(obj, null)) return false; + var casted = (LambdaExpressionCacheKey)obj; + return casted.ToString() == ToString(); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return ToString().GetHashCode(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Properties/AssemblyInfo.cs b/src/Umbraco.Core/Properties/AssemblyInfo.cs index 41a997d394..ee509aaf9b 100644 --- a/src/Umbraco.Core/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Core/Properties/AssemblyInfo.cs @@ -19,4 +19,5 @@ using System.Runtime.InteropServices; [assembly: Guid("130a6b5c-50e7-4df3-a0dd-e9e7eb0b7c5c")] [assembly: InternalsVisibleTo("umbraco")] +[assembly: InternalsVisibleTo("Umbraco.Tests")] diff --git a/src/Umbraco.Core/TypeExtensions.cs b/src/Umbraco.Core/TypeExtensions.cs new file mode 100644 index 0000000000..21506b3b46 --- /dev/null +++ b/src/Umbraco.Core/TypeExtensions.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; + +namespace Umbraco.Core +{ + public static class TypeExtensions + { + + /// + /// Checks if the type is an anonymous type + /// + /// + /// + /// + /// reference: http://jclaes.blogspot.com/2011/05/checking-for-anonymous-types.html + /// + public static bool IsAnonymousType(this Type type) + { + if (type == null) throw new ArgumentNullException("type"); + + + return Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false) + && type.IsGenericType && type.Name.Contains("AnonymousType") + && (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$")) + && (type.Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic; + } + + + public static IEnumerable GetCustomAttributes(this Type type, bool inherited) + where T : Attribute + { + if (type == null) return Enumerable.Empty(); + return type.GetCustomAttributes(typeof (T), inherited).OfType(); + } + + + /// + /// Determines whether the specified type is enumerable. + /// + /// The type. + /// + /// true if the specified type is enumerable; otherwise, false. + /// + public static bool IsEnumerable(this Type type) + { + if (type.IsGenericType) + { + if (type.GetGenericTypeDefinition().GetInterfaces().Contains(typeof(IEnumerable))) + return true; + } + else + { + if (type.GetInterfaces().Contains(typeof(IEnumerable))) + return true; + } + return false; + } + + /// + /// Determines whether [is of generic type] [the specified type]. + /// + /// The type. + /// Type of the generic. + /// + /// true if [is of generic type] [the specified type]; otherwise, false. + /// + public static bool IsOfGenericType(this Type type, Type genericType) + { + Type[] args; + return type.TryGetGenericArguments(genericType, out args); + } + + /// + /// Will find the generic type of the 'type' parameter passed in that is equal to the 'genericType' parameter passed in + /// + /// + /// + /// + /// + public static bool TryGetGenericArguments(this Type type, Type genericType, out Type[] genericArgType) + { + if (type == null) + { + throw new ArgumentNullException("type"); + } + if (genericType == null) + { + throw new ArgumentNullException("genericType"); + } + if (!genericType.IsGenericType) + { + throw new ArgumentException("genericType must be a generic type"); + } + + Func checkGenericType = (@int, t) => + { + if (@int.IsGenericType) + { + var def = @int.GetGenericTypeDefinition(); + if (def == t) + { + return @int.GetGenericArguments(); + } + } + return null; + }; + + //first, check if the type passed in is already the generic type + genericArgType = checkGenericType(type, genericType); + if (genericArgType != null) + return true; + + //if we're looking for interfaces, enumerate them: + if (genericType.IsInterface) + { + foreach (Type @interface in type.GetInterfaces()) + { + genericArgType = checkGenericType(@interface, genericType); + if (genericArgType != null) + return true; + } + } + else + { + //loop back into the base types as long as they are generic + while (type.BaseType != null && type.BaseType != typeof(object)) + { + genericArgType = checkGenericType(type.BaseType, genericType); + if (genericArgType != null) + return true; + type = type.BaseType; + } + + } + + + return false; + + } + + + /// + /// Determines whether the specified actual type is type. + /// + /// + /// The actual type. + /// + /// true if the specified actual type is type; otherwise, false. + /// + public static bool IsType(this Type actualType) + { + return TypeFinder2.IsTypeAssignableFrom(actualType); + } + + public static string GetCacheKeyFromParameters(this MemberInfo info) + { + var methodInfo = info as MethodInfo; + if (methodInfo != null) + return GetCacheKeyFromParameters(methodInfo.GetParameters()); + return string.Empty; + } + + public static string GetCacheKeyFromParameters(IEnumerable parameters) + { + var sb = new StringBuilder(); + sb.Append("("); + foreach (var parameter in parameters) + { + sb.Append(parameter.ParameterType); + sb.Append(" "); + sb.Append(parameter.Name); + sb.Append(","); + } + sb.Append(")"); + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/TypeFinder2.cs b/src/Umbraco.Core/TypeFinder2.cs new file mode 100644 index 0000000000..aa888aafc1 --- /dev/null +++ b/src/Umbraco.Core/TypeFinder2.cs @@ -0,0 +1,645 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Security; +using System.Text; +using System.Threading; +using System.Web; +using System.Web.Compilation; +using System.Web.Hosting; + +namespace Umbraco.Core +{ + /// + /// A utility class to find all classes of a certain type by reflection in the current bin folder + /// of the web application. + /// + public class TypeFinder2 + { + private static readonly ConcurrentDictionary, bool> TypeCheckCache = new ConcurrentDictionary, bool>(); + private static readonly ConcurrentDictionary ValueTypeCache = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary ImplicitValueTypeCache = new ConcurrentDictionary(); + //private static readonly ConcurrentDictionary GetFieldsCache = new ConcurrentDictionary(); + //private static readonly ConcurrentDictionary, PropertyInfo[]> GetPropertiesCache = new ConcurrentDictionary, PropertyInfo[]>(); + private static readonly ConcurrentBag LocalFilteredAssemblyCache = new ConcurrentBag(); + private static readonly ReaderWriterLockSlim LocalFilteredAssemblyCacheLocker = new ReaderWriterLockSlim(); + /// + /// Caches attributed assembly information so they don't have to be re-read + /// + private readonly AttributedAssemblyList _attributedAssemblies = new AttributedAssemblyList(); + + private static ReadOnlyCollection _allAssemblies = null; + private static ReadOnlyCollection _binFolderAssemblies = null; + + private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(); + + /// + /// lazily load a reference to all assemblies and only local assemblies. + /// This is a modified version of: http://www.dominicpettifer.co.uk/Blog/44/how-to-get-a-reference-to-all-assemblies-in-the--bin-folder + /// + /// + /// We do this because we cannot use AppDomain.Current.GetAssemblies() as this will return only assemblies that have been + /// loaded in the CLR, not all assemblies. + /// See these threads: + /// http://issues.umbraco.org/issue/U5-198 + /// http://stackoverflow.com/questions/3552223/asp-net-appdomain-currentdomain-getassemblies-assemblies-missing-after-app + /// http://stackoverflow.com/questions/2477787/difference-between-appdomain-getassemblies-and-buildmanager-getreferencedassembl + /// + internal static IEnumerable GetAllAssemblies() + { + if (_allAssemblies == null) + { + using (new WriteLock(Locker)) + { + try + { + var isHosted = HttpContext.Current != null; + + try + { + if (isHosted) + { + _allAssemblies = new ReadOnlyCollection( + BuildManager.GetReferencedAssemblies().Cast().ToList()); + } + } + catch (InvalidOperationException e) + { + if (!(e.InnerException is SecurityException)) + throw; + } + + _allAssemblies = _allAssemblies ?? new ReadOnlyCollection(AppDomain.CurrentDomain.GetAssemblies().ToList()); + + var codeBase = Assembly.GetExecutingAssembly().CodeBase; + var uri = new Uri(codeBase); + var path = uri.LocalPath; + var binFolder = new DirectoryInfo(Path.GetDirectoryName(path)); + + var dllFiles = Directory.GetFiles(binFolder.FullName, "*.dll", + SearchOption.TopDirectoryOnly).ToList(); + + var binFolderAssemblies = dllFiles.Select(AssemblyName.GetAssemblyName) + .Select(assemblyName => + { + try + { + return _allAssemblies.FirstOrDefault(a => + AssemblyName.ReferenceMatchesDefinition(a.GetName(), assemblyName)); + } + catch (SecurityException) + { + return null; + } + }) + .Where(locatedAssembly => locatedAssembly != null) + .ToList(); + + _binFolderAssemblies = new ReadOnlyCollection(binFolderAssemblies); + } + catch (InvalidOperationException e) + { + if (e.InnerException is SecurityException) + { + // Cannot scan bin folder in medium trust + _binFolderAssemblies = _allAssemblies; + } + } + } + } + + return _allAssemblies; + } + + /// + /// Returns all assemblies loaded in the bin folder that are not in GAC + /// + /// + internal static IEnumerable GetBinFolderAssemblies() + { + if (_binFolderAssemblies == null) GetAllAssemblies(); + return _binFolderAssemblies; + } + + /// + /// Return a list of found assemblies for the AppDomain, excluding the GAC, the ones passed in and excluding the exclusion list filter + /// + /// + /// + internal static IEnumerable GetFilteredDomainAssemblies(IEnumerable excludeFromResults = null) + { + if (excludeFromResults == null) + excludeFromResults = new List(); + + return GetLocalAssembliesWithKnownExclusions().Except(excludeFromResults); + } + + internal static IEnumerable GetLocalAssembliesWithKnownExclusions(IEnumerable excludeFromResults = null) + { + if (LocalFilteredAssemblyCache.Any()) return LocalFilteredAssemblyCache; + using (new WriteLock(LocalFilteredAssemblyCacheLocker)) + { + var assemblies = GetAllAssemblies() + .Where(x => !x.GlobalAssemblyCache + && !KnownAssemblyExclusionFilter.Any(f => x.FullName.StartsWith(f))); + assemblies.ForEach(LocalFilteredAssemblyCache.Add); + } + return LocalFilteredAssemblyCache; + } + + /// + /// Return a list of found local Assemblies in the bin folder exluding the ones passed in and excluding the exclusion list filter + /// + /// + /// + internal static IEnumerable GetFilteredBinFolderAssemblies(IEnumerable excludeFromResults = null) + { + if (excludeFromResults == null) + excludeFromResults = new List(); + + return GetBinFolderAssemblies() + .Where(x => !excludeFromResults.Contains(x) + && !x.GlobalAssemblyCache + && !KnownAssemblyExclusionFilter.Any(f => x.FullName.StartsWith(f))); + } + + /// + /// Return a list of found local Assemblies including plugins and exluding the ones passed in and excluding the exclusion list filter + /// + /// + /// + /// + internal static IEnumerable GetFilteredLocalAssemblies(IEnumerable excludeFromResults = null, + string[] exclusionFilter = null) + { + if (excludeFromResults == null) + excludeFromResults = new List(); + if (exclusionFilter == null) + exclusionFilter = new string[] { }; + + return GetAllAssemblies() + .Where(x => !excludeFromResults.Contains(x) + && !x.GlobalAssemblyCache + && !exclusionFilter.Any(f => x.FullName.StartsWith(f))); + } + + /// + /// this is our assembly filter to filter out known types that def dont contain types we'd like to find or plugins + /// + /// + /// NOTE the comma vs period... comma delimits the name in an Assembly FullName property so if it ends with comma then its an exact name match + /// + internal static readonly string[] KnownAssemblyExclusionFilter = new[] + { + "mscorlib,", + "System.", + "Antlr3.", + "Autofac.", + "Autofac,", + "Castle.", + "ClientDependency.", + "DataAnnotationsExtensions.", + "DataAnnotationsExtensions,", + "Dynamic,", + "HtmlDiff,", + "Iesi.Collections,", + "log4net,", + "Microsoft.", + "Newtonsoft.", + "NHibernate.", + "NHibernate,", + "NuGet.", + "RouteDebugger,", + "SqlCE4Umbraco,", + "Umbraco.Core,", + "umbraco.datalayer,", + "umbraco.editorControls,", + "umbraco.interfaces,", + "umbraco.MacroEngines,", + "umbraco.MacroEngines.", + "umbraco.macroRenderings,", + "umbraco.providers,", + "Umbraco.Web.UI,", + "umbraco.webservices", + "Lucene.", + "Examine,", + "Examine." + }; + + /// + /// Determines whether the type is assignable from the specified implementation , + /// and caches the result across the application using a . + /// + /// The type of the contract. + /// The implementation. + public static bool IsTypeAssignableFrom(Type implementation) + { + return IsTypeAssignableFrom(typeof(TContract), implementation); + } + + /// + /// Determines whether the type is assignable from the specified implementation , + /// and caches the result across the application using a . + /// + /// The type of the contract. + /// The implementation. + /// + /// true if [is type assignable from] [the specified contract]; otherwise, false. + /// + public static bool IsTypeAssignableFrom(Type contract, Type implementation) + { + // NOTE The use of a Tuple<,> here is because its Equals / GetHashCode implementation is literally 10.5x faster than KeyValuePair<,> + return TypeCheckCache.GetOrAdd(new Tuple(contract, implementation), x => x.Item1.IsAssignableFrom(x.Item2)); + } + + /// + /// A cached method to determine whether represents a value type. + /// + /// The implementation. + public static bool IsValueType(Type implementation) + { + return ValueTypeCache.GetOrAdd(implementation, x => x.IsValueType || x.IsPrimitive); + } + + /// + /// A cached method to determine whether is an implied value type (, or a string). + /// + /// The implementation. + public static bool IsImplicitValueType(Type implementation) + { + return ImplicitValueTypeCache.GetOrAdd(implementation, x => IsValueType(implementation) || implementation.IsEnum || implementation == typeof(string)); + } + + public static bool IsTypeAssignableFrom(object implementation) + { + if (implementation == null) throw new ArgumentNullException("implementation"); + return IsTypeAssignableFrom(implementation.GetType()); + } + + + + /// + /// Returns assemblies found in the specified path that the the specified custom attribute type applied to them + /// + /// + /// The assemblies to search on + /// + internal IEnumerable FindAssembliesWithAttribute(IEnumerable assemblies) + where T : Attribute + { + + var foundAssemblies = (from a in assemblies + //if its already registered, then ignore + where !_attributedAssemblies.IsRegistered(a) + let customAttributes = a.GetCustomAttributes(typeof(T), false) + where customAttributes.Any() + select a).ToList(); + //now update the cache + foreach (var a in foundAssemblies) + { + _attributedAssemblies.Add(new AttributedAssembly { Assembly = a, PluginAttributeType = typeof(T), AssemblyFolder = null }); + } + + //We must do a ToList() here because it is required to be serializable when using other app domains. + return _attributedAssemblies + .Where(x => x.PluginAttributeType.Equals(typeof(T)) + && assemblies.Select(y => y.FullName).Contains(x.Assembly.FullName)) + .Select(x => x.Assembly) + .ToList(); + } + + /// + /// Returns found types in assemblies attributed with the specifed attribute type + /// + /// + /// + /// The assemblies to search on + /// + internal IEnumerable FindClassesOfType(IEnumerable assemblies) + where TAssemblyAttribute : Attribute + { + var found = FindAssembliesWithAttribute(assemblies); + return GetAssignablesFromType(found, true); + } + + /// + /// Returns found types in an assembly attributed with the specifed attribute type + /// + /// + /// The type of the assembly attribute. + /// The assembly. + /// + /// + internal IEnumerable FindClassesOfType(Assembly assembly) + where TAssemblyAttribute : Attribute + { + return FindClassesOfType(new[] { assembly }); + } + + /// + /// Searches all filtered local assemblies specified for classes of the type passed in. + /// + /// + /// + public IEnumerable FindClassesOfType() + { + return FindClassesOfType(GetFilteredLocalAssemblies(), true); + } + + /// + /// Searches all assemblies specified for classes of the type passed in. + /// + /// + /// + /// + public IEnumerable FindClassesOfType(IEnumerable assemblyFiles) + { + return FindClassesOfType(assemblyFiles, true); + } + + /// + /// Searches all assemblies specified for classes of the type passed in. + /// + /// + /// + /// Only resolve concrete classes that can be instantiated + /// + public IEnumerable FindClassesOfType(IEnumerable assemblyFiles, bool onlyConcreteClasses) + { + if (assemblyFiles == null) throw new ArgumentNullException("assemblyFiles"); + + var typeAndAssembly = GetAssignablesFromType(assemblyFiles, onlyConcreteClasses); + return GetTypesFromResult(typeAndAssembly); + } + + /// + /// Returns all types found of in the assemblies specified of type T + /// + /// + /// + /// + /// + public IEnumerable FindClassesOfType(IEnumerable assemblies, bool onlyConcreteClasses) + { + if (assemblies == null) throw new ArgumentNullException("assemblies"); + + return GetAssignablesFromType(assemblies, onlyConcreteClasses); + } + + /// + /// Returns all types found of in the assemblies specified of type T + /// + /// + /// + /// + public IEnumerable FindClassesOfType(IEnumerable assemblies) + { + return FindClassesOfType(assemblies, true); + } + + /// + /// Finds the classes with attribute. + /// + /// + /// The assemblies. + /// if set to true only concrete classes. + /// + public IEnumerable FindClassesWithAttribute(IEnumerable assemblies, bool onlyConcreteClasses) + where T : Attribute + { + if (assemblies == null) throw new ArgumentNullException("assemblies"); + + return (from a in assemblies + from t in GetTypesWithFormattedException(a) + where !t.IsInterface && t.GetCustomAttributes(false).Any() && (!onlyConcreteClasses || (t.IsClass && !t.IsAbstract)) + select t).ToList(); + } + + /// + /// Finds the classes with attribute. + /// + /// + /// The assemblies. + /// + public IEnumerable FindClassesWithAttribute(IEnumerable assemblies) + where T : Attribute + { + return FindClassesWithAttribute(assemblies, true); + } + + /// + /// Finds the classes with attribute in filtered local assemblies + /// + /// + /// + public IEnumerable FindClassesWithAttribute() + where T : Attribute + { + return FindClassesWithAttribute(GetFilteredLocalAssemblies()); + } + + #region Internal Methods - For testing + /// + /// Used to find all types in all assemblies in the specified folder + /// + /// + /// + /// + /// + /// + /// This has potential cost alot in performance so it is marked internal and should ONLY be used + /// where absolutely necessary (i.e. Tests) + /// + internal IEnumerable FindClassesOfType(DirectoryInfo folder, bool onlyConcreteClasses) + { + var types = new List(); + types.AddRange(FindClassesOfType(folder.GetFiles("*.dll").Select(x => x.FullName))); + return types; + } + + /// + /// Used to find all types in all assemblies in the specified folder + /// + /// + /// + /// + /// + /// This has potential cost alot in performance so it is marked internal and should ONLY be used + /// where absolutely necessary (i.e. Tests) + /// + internal IEnumerable FindClassesOfType(DirectoryInfo folder) + { + return FindClassesOfType(folder, true); + } + #endregion + + #region Internal Attributed Assembly class + private class AttributedAssembly + { + internal DirectoryInfo AssemblyFolder { get; set; } + internal Assembly Assembly { get; set; } + internal Type PluginAttributeType { get; set; } + } + private class AttributedAssemblyList : List + { + /// + /// Determines if that type has been registered with the folder + /// + /// + /// + /// + internal bool IsRegistered(DirectoryInfo folder) + { + return this.Where(x => x.PluginAttributeType == typeof(T) + && x.AssemblyFolder.FullName.ToUpper() == folder.FullName.ToUpper()).Any(); + } + + /// + /// Determines if the assembly is already registered + /// + /// + /// + /// + internal bool IsRegistered(Assembly assembly) + { + return this.Where(x => x.PluginAttributeType == typeof(T) + && x.Assembly.FullName.ToUpper() == assembly.FullName.ToUpper()) + .Any(); + } + } + + #endregion + + #region Private methods + private static IEnumerable GetTypesFromResult(Dictionary result) + { + return (from type in result + let ass = Assembly.Load(type.Value) + where ass != null + select ass.GetType(type.Key, false)).ToList(); + } + + /// + /// Gets a collection of assignables of type T from a collection of files + /// + /// + /// The files. + /// + /// + private static Dictionary GetAssignablesFromType(IEnumerable files, bool onlyConcreteClasses) + { + return GetTypes(typeof(T), files, onlyConcreteClasses); + } + + /// + /// Gets a collection of assignables of type T from a collection of assemblies + /// + /// + /// + /// + /// + private static IEnumerable GetAssignablesFromType(IEnumerable assemblies, bool onlyConcreteClasses) + { + return GetTypes(typeof(T), assemblies, onlyConcreteClasses); + } + + private static IEnumerable GetTypes(Type assignTypeFrom, IEnumerable assemblies, bool onlyConcreteClasses) + { + return (from a in assemblies + from t in GetTypesWithFormattedException(a) + where !t.IsInterface && assignTypeFrom.IsAssignableFrom(t) && (onlyConcreteClasses ? (t.IsClass && !t.IsAbstract) : true) + select t).ToList(); + } + + private static IEnumerable GetTypesWithFormattedException(Assembly a) + { + try + { + return a.GetExportedTypes(); + } + catch (ReflectionTypeLoadException ex) + { + var sb = new StringBuilder(); + sb.AppendLine("Could not load types from assembly " + a.FullName + ", errors:"); + foreach (var loaderException in ex.LoaderExceptions.WhereNotNull()) + { + sb.AppendLine("Exception: " + loaderException.ToString()); + } + throw new ReflectionTypeLoadException(ex.Types, ex.LoaderExceptions, sb.ToString()); + } + } + + /// + /// Returns of a collection of type names from a collection of assembky files. + /// + /// The assign type. + /// The assembly files. + /// + /// + private static Dictionary GetTypes(Type assignTypeFrom, IEnumerable assemblyFiles, bool onlyConcreteClasses) + { + var result = new Dictionary(); + foreach (var assembly in + assemblyFiles.Where(File.Exists).Select(Assembly.LoadFile).Where(assembly => assembly != null)) + { + try + { + foreach (Type t in + assembly.GetExportedTypes().Where(t => !t.IsInterface && assignTypeFrom.IsAssignableFrom(t) && (onlyConcreteClasses ? (t.IsClass && !t.IsAbstract) : true))) + { + //add the full type name and full assembly name + result.Add(t.FullName, t.Assembly.FullName); + } + } + catch (ReflectionTypeLoadException ex) + { + Debug.WriteLine("Error reading assembly " + assembly.FullName + ": " + ex.Message); + } + } + return result; + } + #endregion + + + ///// + ///// Gets (and caches) discoverable in the current for a given . + ///// + ///// The source. + ///// + //public static FieldInfo[] CachedDiscoverableFields(Type type) + //{ + // return GetFieldsCache.GetOrAdd( + // type, + // x => type + // .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + // .Where(y => !y.IsInitOnly) + // .ToArray()); + //} + + ///// + ///// Gets (and caches) discoverable in the current for a given . + ///// + ///// The source. + ///// true if the properties discovered are readable + ///// true if the properties discovered are writable + ///// true if the properties discovered are indexable + ///// + //public static PropertyInfo[] CachedDiscoverableProperties(Type type, bool mustRead = true, bool mustWrite = true, bool includeIndexed = false) + //{ + // return GetPropertiesCache.GetOrAdd( + // new Tuple(type, mustRead, mustWrite, includeIndexed), + // x => type + // .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + // .Where(y => (!mustRead || y.CanRead) + // && (!mustWrite || y.CanWrite) + // && (includeIndexed || !y.GetIndexParameters().Any())) + // .ToArray()); + //} + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 10179a1284..6848561963 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -32,6 +32,7 @@ + @@ -45,7 +46,11 @@ Properties\SolutionInfo.cs + + + + @@ -57,12 +62,6 @@ - - - {E469A9CE-1BEC-423F-AC44-713CD72457EA} - umbraco.businesslogic - -