rd-net/RdFramework/Base/IRdBindable.cs (274 lines of code) (raw):

using System; using System.Collections; using System.Collections.Generic; using System.Linq; using JetBrains.Collections.Viewable; using JetBrains.Diagnostics; using JetBrains.Lifetimes; using JetBrains.Rd.Impl; using JetBrains.Rd.Util; using JetBrains.Serialization; using JetBrains.Util; namespace JetBrains.Rd.Base { public interface IRdDynamic { RName Location { get; } IProtocol? TryGetProto(); bool TryGetSerializationContext(out SerializationCtx ctx); } public static class RdDynamicEx { public static IProtocol GetProtoOrThrow(this IRdDynamic dynamic) { return dynamic.TryGetProto() ?? throw new ProtocolNotBoundException(dynamic.ToString()); } } public interface IPrintable { void Print(PrettyPrinter printer); } public interface IRdWireable : IRdDynamic { RdId RdId { get; } void OnWireReceived(UnsafeReader reader, IRdWireableDispatchHelper dispatchHelper); } public interface IRdWireableDispatchHelper { RdId RdId { get; } Lifetime Lifetime { get; } public void Dispatch(IScheduler? scheduler, Action action); } public static class RdWireableDispatchHelperEx { public static void Dispatch(this IRdWireableDispatchHelper helper, IScheduler? scheduler, Action action) { helper.Dispatch(scheduler, action); } public static void Dispatch(this IRdWireableDispatchHelper helper, Action action) { helper.Dispatch(null, action); } } public interface IRdBindable : IRdDynamic, IPrintable { RdId RdId { get; set; } void PreBind(Lifetime lf, IRdDynamic parent, string name); void Bind(); void Identify(IIdentities identities, RdId id); } internal readonly ref struct AllowBindCookie { private readonly bool myCreated; [ThreadStatic] public static int IsBindAllowedCount; public static bool IsBindAllowed => IsBindAllowedCount > 0; public static bool IsBindNotAllowed => !IsBindAllowed; private AllowBindCookie(bool created) { myCreated = created; } public static AllowBindCookie Create() { IsBindAllowedCount++; return new AllowBindCookie(true); } public void Dispose() { if (myCreated) IsBindAllowedCount--; } } public static class RdBindableEx { #region Bind internal static void PreBindPolymorphic(this object? value, Lifetime lifetime, IRdDynamic parent, string name) { if (value is IRdBindable rdBindable) rdBindable.PreBind(lifetime, parent, name); else //Don't remove 'else'. RdList is bindable and collection simultaneously. (value as IEnumerable)?.PreBind0(lifetime, parent, name); } internal static void BindPolymorphic(this object? value) { if (value is IRdBindable rdBindable) rdBindable.Bind(); else //Don't remove 'else'. RdList is bindable and collection simultaneously. (value as IEnumerable)?.Bind0(); } internal static class FastIsBindable<T> { // ReSharper disable once StaticMemberInGenericType public static readonly bool Value = Calculate(); private static bool Calculate() { var type = typeof(T); if (type.IsValueType) return false; var rdBindableType = typeof(IRdBindable); if (rdBindableType.IsAssignableFrom(type)) return true; if (type.IsArray && type.GetElementType() is { } elementType && rdBindableType.IsAssignableFrom(elementType)) return true; if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)) { var argument = type.GetGenericArguments().Single(); if (rdBindableType.IsAssignableFrom(argument)) return true; } return false; } } internal static bool IsBindable<T>(this T obj) { if (obj == null) return false; if (FastIsBindable<T>.Value) return true; switch (obj) { case IRdBindable _: return true; case IEnumerable enumerable when !(typeof(T).IsValueType && obj.Equals((T)default!)): { foreach (var item in enumerable) return item is IRdBindable; break; } } return false; } private static void PreBind0(this IEnumerable? items, Lifetime lifetime, IRdDynamic parent, string name) { if (items == null) return; var cnt = 0; foreach (var item in items) { if (item is not IRdBindable bindable) return; bindable.PreBindEx(lifetime, parent, name + "[" + cnt++ + "]"); } } private static void Bind0(this IEnumerable? items) { if (items == null) return; foreach (var item in items) { if (item is not IRdBindable bindable) return; bindable.BindEx(); } } public static void PreBindEx<T>(this T? value, Lifetime lifetime, IRdDynamic parent, string name) where T : IRdBindable { if (value != null) value.PreBind(lifetime, parent, name); } public static void BindEx<T>(this T? value) where T : IRdBindable { if (value != null) value.Bind(); } public static void BindTopLevel<T>(this T? value, Lifetime lifetime, IProtocol parent, string name) where T : IRdBindable { if (value != null) { value.PreBind(lifetime, parent, name); value.Bind(); } } #endregion #region Identify internal static void IdentifyPolymorphic(this object? value, IIdentities ids, RdId id) { if (value is IRdBindable rdBindable) rdBindable.Identify(ids, id); else (value as IEnumerable).Identify0(ids, id); } private static void Identify0(this IEnumerable? items, IIdentities ids, RdId id) { if (items == null) return; var i = 0; foreach (var x in items) { (x as IRdBindable).IdentifyEx(ids, id.Mix(i++)); } } public static void IdentifyEx<T>(this T? value, IIdentities ids, RdId id) where T : IRdBindable { if (value != null) value.Identify(ids, id); } //PLEASE DON'T MERGE these two methods into one with IEnumerable<T>, just believe me public static void IdentifyEx<T>(this List<T>? items, IIdentities ids, RdId id) where T : IRdBindable { items.Identify0(ids, id); } public static void IdentifyEx<T>(this T[]? items, IIdentities ids, RdId id) where T : IRdBindable { items.Identify0(ids, id); } #endregion } public static class PrintableEx { public static void PrintEx(this object? me, PrettyPrinter printer) { var printable = me as IPrintable; if (printer.BufferExceeded) return; if (printable != null) printable.Print(printer); else switch (me) { case null: printer.Print("<null>"); break; case string _: printer.Print("\"" + me + "\""); break; case IEnumerable enumerable: { if (!printer.PrintContent) break; printer.Print("["); using (printer.IndentCookie()) { var en = enumerable.GetEnumerator(); var count = 0; var maxPrint = printer.CollectionMaxLength; while (en.MoveNext()) { if (printer.BufferExceeded) return; if (count < maxPrint) { printer.Println(); en.Current.PrintEx(printer); } count ++; } if (count > maxPrint) { printer.Println(); printer.Print("... and " + (count - maxPrint) + " more"); } if (count > 0) printer.Println(); else printer.Print("<empty>"); } printer.Print("]"); break; } default: printer.Print(me.ToString()); break; } } public static string PrintToString(this object? me) { var prettyPrinter = new PrettyPrinter(); me.PrintEx(prettyPrinter); return prettyPrinter.ToString(); } public static string PrintToStringNoLimits(this object? me) { var prettyPrinter = new PrettyPrinter { CollectionMaxLength = Int32.MaxValue }; me.PrintEx(prettyPrinter); return prettyPrinter.ToString(); } } }