using System.Collections; using System.Collections.Generic; using System.Linq; namespace OpenTap { class ParameterMemberAnnotator : IAnnotator { public double Priority => 20; class SubAvailable : IAvailableValuesAnnotation { public IEnumerable AvailableValues => sub.Get().AvailableValues; AnnotationCollection sub; public SubAvailable(AnnotationCollection subcol) => sub = subcol; } class SubSuggested : ISuggestedValuesAnnotation { AnnotationCollection subcol; public SubSuggested(AnnotationCollection subcol) => this.subcol = subcol; public IEnumerable SuggestedValues => subcol.Get().SuggestedValues; } class SubAccess : IAccessAnnotation { private AnnotationCollection sub; public bool IsReadOnly => sub.Get().IsReadOnly; public bool IsVisible => sub.Get().IsVisible; public SubAccess(AnnotationCollection sub) => this.sub = sub; } class SubMember : IOwnedAnnotation { readonly AnnotationCollection sub; readonly bool bigList; readonly IParameterMemberData param; public SubMember(AnnotationCollection sub, bool bigList, IParameterMemberData param) { this.sub = sub; this.bigList = bigList; this.param = param; } public void Read(object source) { sub.Read(); } public void Write(object source) { sub.Write(); if (bigList) // for big lists, we try to copy the values. param.SetValue(source, param.GetValue(source)); } } // Since having multiple parameter member annotations nested, creates multiple levels of parameter member annotation // things can become incredibly slow when this happens. // it is much faster if we can just crush all the multiple levels of IParameterMemberData into on layer // of no-IParameterMemberData. // This should be safe since IParameterMemberDatas generally does not have any information connected to them // that are not already present on the sub-MemberData. static List<(object, IMemberData)> GetRootMembers(IParameterMemberData member) { List<(object, IMemberData)> nonParameters = new List<(object, IMemberData)>(); Stack workQueue = new Stack(); workQueue.Push(member); while (workQueue.Count > 0) { var mem = workQueue.Pop(); foreach (var (s, m) in mem.ParameterizedMembers) { if (m is IParameterMemberData p) workQueue.Push(p); else nonParameters.Add((s, m)); } } return nonParameters; } public void Annotate(AnnotationCollection annotation) { var member = annotation.Get()?.Member as IParameterMemberData; if (member == null) return; // if the list is really huge, the next operations will be quite complex. // if we assume that the elements are simple in nature, we can regain that performance // by only merging a subset of them and then just copy the values for the rest of the objects. bool bigList = member.ParameterizedMembers.Count() > 100; var parameterizedMembers = GetRootMembers(member); var items = parameterizedMembers.Select(x => x.Item1).Take(100).ToArray(); var cache = annotation.Get(true); if (cache == null) { cache = new AnnotationCache(); annotation.ParentAnnotation?.Add(cache); } var subannotation = cache.Annotate(items) ?? AnnotationCollection.Annotate(items.Length == 1 ? items[0] : items); annotation.Add(new SubMember(subannotation, bigList, member)); var subMembers = subannotation.Get(); var firstmem = parameterizedMembers.First().Item2; var thismember = subMembers.Members.FirstOrDefault(x => x.Get().Member == firstmem); if (thismember == null) return; annotation.RemoveType(); annotation.Add(new SubAccess(thismember)); IAvailableValuesAnnotation avail = annotation.Get(); if (avail != null) annotation.Add(new SubAvailable(thismember)); ISuggestedValuesAnnotation suggested = annotation.Get(); if (suggested != null) annotation.Add(new SubSuggested(thismember)); } } }