Esempio n. 1
0
 private static void PostWiringInializeAddHandler(PostWiringInitializeDelegate handler)
 {
     // we can be called by PostWiringInitialize when a PostWiringInitialize function does some wiring.
     // Put new handlers at the end of the list (but make sure they are not in the list first)
     {
         if (!PostWiringInitializeDelegateList.Contains(handler))
         {
             PostWiringInitializeDelegateList.Add(handler);
         }
     }
 }
Esempio n. 2
0
        /// <Summary>
        /// wireTo is an extension method on the type object
        /// Important method that wires and connects instances of classes that have ports by matching interfaces (with optional port names).
        /// If object A (this) has a private field of an interface, and object B implements the interface, then wire them together using reflection.
        /// Returns this for fluent style programming.
        /// </summary>
        /// ------------------------------------------------------------------------------------------------------------------
        /// WireTo method understanding what it doess:
        /// <param name="A">
        /// The object on which the method is called is the object being wired from. It must have a private field of the interface type.
        /// </param>
        /// <param name="B">The object being wired to. It must implement the interface)</param>
        /// <returns>this to support fluent programming style which allows multiple wiring to the same A object with dot operators</returns>
        /// <remarks>
        /// 1. only wires compatible interfaces, A uses the interface and B implements the interface
        /// 2. interface field must be private (this prevents confusion when using the abstraction of seeing a public field)
        /// 3. can only wire a single matching interface per call (wires the first one it finds in class A starting at the top of the class)
        /// 4. skips ports in class A that are already wired
        /// 5. you can overide the above order by specifying port names as a second parameter to the wireTo method
        /// 6. looks for list as well (be careful of a list of interface blocking other fields of the same interfaces type lower down from ever being wired)
        /// ------------------------------------------------------------------------------------------------------------------
        /// Also looks for a method: "private void PostWiringInitialize()" in either or both objects being wired.
        /// This method is added to a list of such methods, and all are called once when our method "public static void PostWiringInitialize()" is called (which should be called immediately after all wiring code has executed).
        /// PostWiringInitialize() methods are used to complete any initializaation that needs to be done inside a domain abstraction instance after all the wiring has been completed but before the application begins running.
        /// [Note that PostWiringInitialize methods in different domain abstraction instances are called in a uncontrolled order, so only do things that don't interact with other instances.
        /// Intialization that needs to be done in an ordered fashion: suggest they should be done using Initialize ports on domain abstractions.
        /// Instances of these can then be explicitly wired in the application diagram to a chain of IEvent connectors (Activity diagram programming paradigm).
        /// Alternatively these domain abstractions could have an Initialize input port and an Initialize output port which can then be wired in a chain (the approach of function blocks)]
        /// ------------------------------------------------------------------------------------------------------------------
        /// Whenever a port is wired on the A side, it looks for a private method with the name of the port and ending with the word "Initialize" and calls that method immediately after doing the wiring.
        /// For example if a port exists "private IEvent output", and a method exists "private void outputInitialize()" then outputInitialize gets called after output has been assigned to its wired object B.
        /// These methods are typically used in the domain abstraction to attach a C# event handler to any C# events that are in programming paradigm interface that has just been wired to.
        /// ------------------------------------------------------------------------------------------------------------------
        /// If A has two private fields of the same interface, the first compatible B object wired goes to the first one and the second compatible B object wired goes to the second.
        /// If A has multiple private interfaces of different types, only the first matching interface that B implements will be wired.
        /// By default, only one interface is wired between A and B
        /// To override this behaviour you can get give multiple interfaces in A a prefix "Pn_" where n is 0..9:
        /// Then all fields with the same prfix are considered to be a single logical port.
        /// Then a single wiring operation will wire all fields with a consistent port prefix to the same B object.
        /// These remarks apply only to single fields, not Lists.
        /// e.g.
        /// private IX client1X;
        /// private IY client1Y;
        /// private IZ client2;
        /// Here we want to wire the first two fields to the same object.
        /// So name the fields like this:
        /// private IX P1_client1X;
        /// private IY P1_client1Y;
        /// private IZ client2;
        /// </remarks>
        /// Future owrk: Should WireTo be an extension method on an object called "DomainAbstraction" just so it doesn't become available to call on all other normal objects?
        /// This would mean that all DomainAbstraction classes must inherit from the completely empty abstract class "DomainAbstraction", which wouldn't be so bad.
        public static T WireTo <T>(this T A, object B, string APortName = null, bool reverse = false)
        {
            string multiportExceptionMessage = $"The following wiring failed because the two instances are already wired together by another port.";

            multiportExceptionMessage += "\nPlease use a new WireTo with this additional port name specified:";

            // Get the two instance name first for the Debug Output WriteLines
            var AinstanceName = A?.GetType().GetProperties().FirstOrDefault(f => f.Name == "InstanceName")?.GetValue(A);

            if (AinstanceName == null)
            {
                AinstanceName = A?.GetType().GetFields().FirstOrDefault(f => f.Name == "InstanceName")?.GetValue(A);
            }
            var BinstanceName = B?.GetType().GetProperties().FirstOrDefault(f => f.Name == "InstanceName")?.GetValue(B);

            if (BinstanceName == null)
            {
                BinstanceName = B?.GetType().GetFields().FirstOrDefault(f => f.Name == "InstanceName")?.GetValue(B);
            }

            string DiagnosticLine = $"Failed to wire {A?.GetType().Name}[{AinstanceName}].{APortName} to {B?.GetType().Name}[{BinstanceName}]";

            if (A == null)
            {
                throw new ArgumentException("A cannot be null " + DiagnosticLine);
            }
            if (B == null)
            {
                throw new ArgumentException("B cannot be null " + DiagnosticLine);
            }



            // achieve the following via reflection
            // A.field = (<type of interface>)B;
            // A.list.Add( (<type of interface>)B );


            var BType       = B.GetType();
            var AfieldInfos = A.GetType().GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
                              .Where(f => (APortName == null || f.Name == APortName) && (!reverse ^ EndsIn_B(f.Name))).ToList(); // find the fields that the name meets all criteria
                                                                                                                                 // TODO: when not reverse ports ending in _B should be excluded

            var wiredSomething = false;

            firstPortName = null;
            foreach (var BimplementedInterface in BType.GetInterfaces())                                                         // consider every interface implemented by B
            {
                var AfieldInfo = AfieldInfos.FirstOrDefault(f => f.FieldType == BimplementedInterface && f.GetValue(A) == null); // find the first field in A that matches the interface type of B
                                                                                                                                 // TODO: the list case below should have the SamePort constraint as well

                // look for normal private fields first
                if (AfieldInfo != null)  // there is a match
                {
                    if (SamePort(AfieldInfo.Name))
                    {
                        if (wiredSomething)
                        {
                            string exceptionMessage = multiportExceptionMessage + "\n" + WiringToString(A, B, AfieldInfo);
                            // throw new Exception(exceptionMessage);
                        }

                        if (!preventRecursion && dictionary.ContainsKey(AfieldInfo.FieldType))
                        {
                            preventRecursion = true;
                            dictionary[AfieldInfo.FieldType](A, B, AfieldInfo.Name);
                            preventRecursion = false;
                        }
                        else
                        {
                            AfieldInfo.SetValue(A, B);  // do the wiring
                        }
                        wiredSomething = true;

                        // see if there is a PostWiring function associated with the port and call it.
                        var m = A.GetType().GetMethod($"{AfieldInfo.Name}Initialize", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                        if (m != null)
                        {
                            PostWiringInitializeDelegate handler = (PostWiringInitializeDelegate)Delegate.CreateDelegate(typeof(PostWiringInitializeDelegate), A, m);
                            handler();
                        }

                        diagnosticOutput?.Invoke(WiringToString(A, B, AfieldInfo));
                    }
                    continue;  // could be more than one interface to wire
                }

                // do the same as above for private fields that are a list of the interface of the matching type
                foreach (var AlistFieldInfo in AfieldInfos)
                {
                    if (!AlistFieldInfo.FieldType.IsGenericType) //not matching interface
                    {
                        continue;
                    }
                    var AListFieldValue = AlistFieldInfo.GetValue(A);

                    var AListGenericArguments = AlistFieldInfo.FieldType.GetGenericArguments();
                    if (AListGenericArguments.Length != 1)
                    {
                        continue;                                                         // A list should only have one type anyway
                    }
                    if (AListGenericArguments[0].IsAssignableFrom(BimplementedInterface)) // JRS: There was some case where == didn't work, maybe in the gamescoring application
                    {
                        if (AListGenericArguments[0] != BimplementedInterface)
                        {
                            var g = AListGenericArguments[0];
                            //if (g != typeof(object)) throw new Exception($"Different types {g} {AListGenericArguments[0]} {typeof(object)}");
                            continue;
                        }
                        if (AListFieldValue == null)
                        {
                            var    listType  = typeof(List <>);
                            Type[] listParam = { BimplementedInterface };
                            AListFieldValue = Activator.CreateInstance(listType.MakeGenericType(listParam));
                            if (wiredSomething)
                            {
                                string exceptionMessage = multiportExceptionMessage + "\n" + WiringToString(A, B, AListFieldValue);
                                throw new Exception(exceptionMessage);
                            }

                            AlistFieldInfo.SetValue(A, AListFieldValue);
                        }

                        AListFieldValue.GetType().GetMethod("Add").Invoke(AListFieldValue, new[] { B });
                        wiredSomething = true;

                        // see if there is a PostWiringInitialize function associated with the port and call it.
                        var m = A.GetType().GetMethod($"{AlistFieldInfo.Name}Initialize", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                        if (m != null)
                        {
                            PostWiringInitializeDelegate handler = (PostWiringInitializeDelegate)Delegate.CreateDelegate(typeof(PostWiringInitializeDelegate), A, m);
                            handler();
                        }

                        diagnosticOutput?.Invoke(WiringToString(A, B, AlistFieldInfo));
                        break;
                    }
                }
            }

            if (!reverse && !wiredSomething)
            {
                if (APortName != null)
                {
                    // a specific port was specified so see if the port was already wired
                    var AfieldInfo = AfieldInfos.FirstOrDefault();
                    if (AfieldInfo?.GetValue(A) != null)
                    {
                        throw new Exception($"Port already wired {A.GetType().Name}[{AinstanceName}].{APortName} to {BType.Name}[{BinstanceName}]");
                    }
                }
                throw new Exception($"Failed to wire {A.GetType().Name}[{AinstanceName}].\"{APortName}\" to {BType.Name}[{BinstanceName}]");
            }

            var method = A.GetType().GetMethod("PostWiringInitialize", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

            if (method != null)
            {
                InitializeDelegate handler = (InitializeDelegate)Delegate.CreateDelegate(typeof(InitializeDelegate), A, method);
                Initialize -= handler;  // instances can be wired to/from more than once, so only register their PostWiringInitialize once
                Initialize += handler;
            }

            /*
             * method = B.GetType().GetMethod("PostWiringInitialize", System.Reflection.BindingFlags.NonPublic);
             * if (method != null)
             * {
             *  InitializeDelegate handler = (InitializeDelegate)Delegate.CreateDelegate(typeof(InitializeDelegate), B, method);
             *  Initialize += handler;
             * }
             */
            return(A);
        }