using System; using System.Collections.Generic; using System.Linq; namespace Unity.VisualScripting { public sealed class CodebaseSubset { private CodebaseSubset(IEnumerable types, MemberFilter memberFilter, TypeFilter memberTypeFilter = null) { Ensure.That(nameof(types)).IsNotNull(types); Ensure.That(nameof(memberFilter)).IsNotNull(memberFilter); this.types = types.ToHashSet(); this.memberFilter = memberFilter; this.memberTypeFilter = memberTypeFilter; } private CodebaseSubset(IEnumerable typeSet, TypeFilter typeFilter, MemberFilter memberFilter, TypeFilter memberTypeFilter = null) { Ensure.That(nameof(typeSet)).IsNotNull(typeSet); Ensure.That(nameof(typeFilter)).IsNotNull(typeFilter); Ensure.That(nameof(memberFilter)).IsNotNull(memberFilter); this.typeSet = typeSet; this.typeFilter = typeFilter; this.memberFilter = memberFilter; this.memberTypeFilter = memberTypeFilter; } private readonly object @lock = new object(); private bool ready; public IEnumerable typeSet { get; } public TypeFilter typeFilter { get; } public MemberFilter memberFilter { get; } public TypeFilter memberTypeFilter { get; } public HashSet types { get; private set; } public HashSet members { get; private set; } public void Cache() { lock (@lock) { if (ready) { return; } ProgressUtility.DisplayProgressBar("Analyzing codebase...", null, 0); if (typeSet != null) { types = typeSet.Where(typeFilter.ValidateType) .ToHashSet(); } members = new HashSet(); var progress = 0f; foreach (var type in types) { ProgressUtility.DisplayProgressBar("Analyzing codebase...", type.DisplayName(), progress++ / types.Count); if (type.IsEnum) { continue; } members.UnionWith(FilterMembers(type)); } ProgressUtility.ClearProgressBar(); ready = true; } } public bool ValidateType(Type type) { return types.Contains(type); } public bool ValidateMember(Member member) { return ValidateType(member.targetType) && memberFilter.ValidateMember(member.info, memberTypeFilter); } public IEnumerable FilterMembers(Type type) { foreach (var member in type.GetMembers(memberFilter.validBindingFlags) .Where(member => memberFilter.validMemberTypes.HasFlag(member.MemberType) && memberFilter.ValidateMember(member, memberTypeFilter)) .Select(member => member.ToManipulator(type))) { yield return member; } if (memberFilter.Methods && memberFilter.Extensions) { foreach (var member in type.GetExtensionMethods(memberFilter.Inherited) .Where(method => ValidateType(method.DeclaringType) && memberFilter.ValidateMember(method, memberTypeFilter)) .Select(member => member.ToManipulator(type))) { yield return member; } } } private static readonly Dictionary cache = new Dictionary(); internal static CodebaseSubset Get(IEnumerable types, MemberFilter memberFilter, TypeFilter memberTypeFilter = null) { var query = new Query(types, memberFilter, memberTypeFilter); if (!cache.ContainsKey(query)) { cache.Add(query, new CodebaseSubset(types, memberFilter, memberTypeFilter)); } return cache[query]; } internal static CodebaseSubset Get(IEnumerable typeSet, TypeFilter typeFilter, MemberFilter memberFilter, TypeFilter memberTypeFilter = null) { var query = new Query(typeSet, typeFilter, memberFilter, memberTypeFilter); if (!cache.ContainsKey(query)) { cache.Add(query, new CodebaseSubset(typeSet, typeFilter, memberFilter, memberTypeFilter)); } return cache[query]; } private struct Query { public IEnumerable types { get; } public IEnumerable typeSet { get; } public TypeFilter typeFilter { get; } public MemberFilter memberFilter { get; } public TypeFilter memberTypeFilter { get; } public Query(IEnumerable typeSet, TypeFilter typeFilter, MemberFilter memberFilter, TypeFilter memberTypeFilter) { types = null; this.typeSet = typeSet; this.typeFilter = typeFilter; this.memberFilter = memberFilter; this.memberTypeFilter = memberTypeFilter; } public Query(IEnumerable types, MemberFilter memberFilter, TypeFilter memberTypeFilter) { typeSet = null; typeFilter = null; this.types = types; this.memberFilter = memberFilter; this.memberTypeFilter = memberTypeFilter; } public override int GetHashCode() { unchecked { var hash = 17; hash = hash * 23 + (typeSet != null).GetHashCode(); if (typeSet != null) { foreach (var type in typeSet) { hash = hash * 23 + (type?.GetHashCode() ?? 0); } } else { foreach (var type in types) { hash = hash * 23 + (type?.GetHashCode() ?? 0); } } hash = hash * 23 + (typeFilter?.GetHashCode() ?? 0); hash = hash * 23 + (memberFilter?.GetHashCode() ?? 0); hash = hash * 23 + (memberTypeFilter?.GetHashCode() ?? 0); return hash; } } } } }