using System;
using System.Collections.Generic;
using System.Linq;
namespace Unity.VisualScripting
{
public sealed class CodebaseSubset
{
private CodebaseSubset(IEnumerable<Type> 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<Type> 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<Type> typeSet { get; }
public TypeFilter typeFilter { get; }
public MemberFilter memberFilter { get; }
public TypeFilter memberTypeFilter { get; }
public HashSet<Type> types { get; private set; }
public HashSet<Member> 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<Member>();
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<Member> 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<Query, CodebaseSubset> cache = new Dictionary<Query, CodebaseSubset>();
internal static CodebaseSubset Get(IEnumerable<Type> 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<Type> 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<Type> types { get; }
public IEnumerable<Type> typeSet { get; }
public TypeFilter typeFilter { get; }
public MemberFilter memberFilter { get; }
public TypeFilter memberTypeFilter { get; }
public Query(IEnumerable<Type> typeSet, TypeFilter typeFilter, MemberFilter memberFilter, TypeFilter memberTypeFilter)
{
types = null;
this.typeSet = typeSet;
this.typeFilter = typeFilter;
this.memberFilter = memberFilter;
this.memberTypeFilter = memberTypeFilter;
}
public Query(IEnumerable<Type> 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;
}
}
}
}
}