public void TryCompare(int p1, int p2, IxCoreColleague c1, IxCoreColleague c2) { var sut = new TupleComparer(); var tuple1 = Tuple.Create (p1, c1); var tuple2 = Tuple.Create (p2, c2); Assert.That(sut.Compare(tuple1, tuple2), Is.LessThan(0)); Assert.That(sut.Compare(tuple2, tuple1), Is.GreaterThan(0)); }
public void TryCompare(int p1, int p2, IxCoreColleague c1, IxCoreColleague c2) { var sut = new TupleComparer(); var tuple1 = Tuple.Create(p1, c1); var tuple2 = Tuple.Create(p2, c2); Assert.That(sut.Compare(tuple1, tuple2), Is.LessThan(0)); Assert.That(sut.Compare(tuple2, tuple1), Is.GreaterThan(0)); }
/// <summary> /// Constructor with paramters being required. /// </summary> /// <param name="mediator">Mediator that will handle the collegue during its temporary liketime.</param> /// <param name="temporaryColleague"></param> public TemporaryColleagueParameter(Mediator mediator, IxCoreColleague temporaryColleague, bool shouldDispose) { if (mediator == null) { throw new ArgumentNullException("'mediator' parameter cannot be null."); } if (temporaryColleague == null) { throw new ArgumentNullException("'temporaryColleague' parameter cannot be null."); } m_mediator = mediator; m_temporaryColleague = temporaryColleague; m_shouldDispose = shouldDispose; }
//from IUIMenuAdapter // public ContextMenu MakeContextMenu (ChoiceGroup group) // { // CommandBarContextMenu menu= new CommandBarContextMenu(); // menu.Tag = group; // group.ReferenceWidget = menu; // menu.Popup += new System.EventHandler(group.OnDisplay); // // return (ContextMenu)menu; // } public void ShowContextMenu(ChoiceGroup group, Point location, TemporaryColleagueParameter temporaryColleagueParam, MessageSequencer sequencer) { using (CommandBarContextMenu menu = new CommandBarContextMenu()) { menu.Tag = group; group.ReferenceWidget = menu; menu.Popup += new System.EventHandler(group.OnDisplay); // Pre-menu process two optional paremeters. if (temporaryColleagueParam != null) { temporaryColleagueParam.Mediator.AddColleague(temporaryColleagueParam.TemporaryColleague); } bool resume = false; if (sequencer != null) { resume = sequencer.PauseMessageQueueing(); } // NB: This is *always* modal, as it doesn't return until it closes. menu.Show(null, location); // Post-menu process two optional paremeters.s if (temporaryColleagueParam != null) { IxCoreColleague colleague = temporaryColleagueParam.TemporaryColleague; temporaryColleagueParam.Mediator.RemoveColleague(colleague); if (temporaryColleagueParam.ShouldDispose && colleague is IDisposable) { (colleague as IDisposable).Dispose(); } } if (sequencer != null && resume) { sequencer.ResumeMessageQueueing(); } } }
/// <summary> /// Executes in two distinct scenarios. /// /// 1. If disposing is true, the method has been called directly /// or indirectly by a user's code via the Dispose method. /// Both managed and unmanaged resources can be disposed. /// /// 2. If disposing is false, the method has been called by the /// runtime from inside the finalizer and you should not reference (access) /// other managed objects, as they already have been garbage collected. /// Only unmanaged resources can be disposed. /// </summary> /// <param name="disposing"></param> /// <remarks> /// If any exceptions are thrown, that is fine. /// If the method is being done in a finalizer, it will be ignored. /// If it is thrown by client code calling Dispose, /// it needs to be handled by fixing the bug. /// /// If subclasses override this method, they should call the base implementation. /// </remarks> protected override void Dispose(bool disposing) { //Debug.WriteLineIf(!disposing, "****************** " + GetType().Name + " 'disposing' is false. ******************"); // Can be called more than once, but not run more than once. if (m_isDisposed) return; if (disposing) { // Dispose managed resources here. // Use a copy of the m_colleagues Set, // since the Dispose methods on the colleague should remove itself from m_colleagues, // which will cause an exception to be throw (list changed while spinning through it. Set<IxCoreColleague> copyOfColleagues = new Set<IxCoreColleague>(m_colleagues.ToArray()); m_colleagues.Clear(); // Just get rid of them now. foreach (IxCoreColleague icc in copyOfColleagues) { if (icc is IDisposable) { // Is the class marked with [XCore.MediatorDispose], // or is it in the temporary colleague holder? object[] attrs = icc.GetType().GetCustomAttributes(typeof(MediatorDisposeAttribute), true); if ((attrs != null && attrs.Length > 0) || m_temporaryColleague == icc) { (icc as IDisposable).Dispose(); } } } copyOfColleagues.Clear(); if (m_propertyTable != null) m_propertyTable.Dispose(); if (m_commandSet != null) m_commandSet.Dispose(); if (m_pathVariables != null) m_pathVariables.Clear(); if (m_disposedColleagues != null) m_disposedColleagues.Clear(); if (m_jobs != null) m_jobs.Clear(); if (m_pendingjobs != null) m_pendingjobs.Clear(); if (m_MethodsOnAnyColleague != null) m_MethodsOnAnyColleague.Clear(); m_idleQueue.Dispose(); } // Dispose unmanaged resources here, whether disposing is true or false. m_mainWndPtr = IntPtr.Zero; m_MethodsOnAnyColleague = null; m_pendingjobs = null; m_jobs = null; m_disposedColleagues = null; m_temporaryColleague = null; m_propertyTable = null; m_commandSet = null; m_colleagues = null; m_pathVariables = null; /* It is illegal to try to access managed stuff in this part of the Dispose method. #if DEBUG //DebugMsg("-- Number of calls to the InvokeRecursively method = " + m_invokeCount.ToString()); DebugMsg("-- Number of saved calls to Type.GetMethod = " + m_SavedCalls.ToString()); DebugMsg("-- Mediator MsgHash info: count=" + m_MethodsNOTonColleagues.Count + " mx depth=" + m_MethodsCount); DebugMsg("-- Mediator - Calls to check for method on colleague: " + m_MethodChecks); #endif */ m_isDisposed = true; base.Dispose(disposing); }
public void RemoveColleague(IxCoreColleague colleague) { CheckDisposed(); // No need to check if m_colleagues is null. // if it hasn't been dispoded, as it will always be non-null. // If it has been disposed, then the caller should be fixed to be better // behaved about calling disposed objects. (Read: it shouldn't ever call code on disposed objects.) //if (m_colleagues != null) m_colleagues.Remove(colleague); }
public void AddTemporaryColleague(IxCoreColleague colleague) { CheckDisposed(); if (m_temporaryColleague != null) RemoveColleague(m_temporaryColleague); m_temporaryColleague = colleague; // only keep one temporary colleague at a time (menu based) AddColleague(m_temporaryColleague); }
public void AddColleague(IxCoreColleague colleague) { CheckDisposed(); // Note: m_colleagues is now a Set, so would ignore the attempt to add it again. // The problem with that is it is really a programming error to add them more than once. // So, we will keep the exception. if (m_colleagues.Contains(colleague)) throw new ApplicationException ("This object is already in the list of colleagues."); m_colleagues.Add(colleague); }
//static System.Diagnostics.Stopwatch ttime = new Stopwatch(); /// <summary> /// /// </summary> /// <param name="colleague"></param> /// <param name="methodName"></param> /// <param name="parameterTypes"></param> /// <param name="parameterList"></param> /// <param name="previous">to catch infinite loops</param> private bool InvokeRecursively(IxCoreColleague colleague, string methodName, Type[] parameterTypes, object[] parameterList, HashSet<object> previous, bool stopWhenHandled, bool justCheckingForReceivers) { if (!ProcessMessages) return true; bool handled = false; // Trace.Indent(); if (invokeSwitch.TraceVerbose) { Trace.WriteLine("InvokeRecursively: methodName=<" + methodName + "> colleague=<" + colleague.ToString() + ">", invokeSwitch.DisplayName); } m_invokeCount++; //////// THIS IS TESTING CODE ADDED AND COMMENTED AND NEEDS TO BE REVERTED WHEN DONE>>>>>>>> //////// ><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<> ////////ttime.Start(); ////////ttime.Stop(); ////////if ((m_invokeCount % 1000) == 0) ////////{ //////// TimeSpan ts = ttime.Elapsed; //////// string tsString = String.Format("{0:00}.{1:0000}({2})", //////// ts.Seconds, ts.Milliseconds, ts.Ticks ); //////// Trace.WriteLine(tsString + " ColleagueHasBeenDisposed(" + colleague.ToString() + ")="+chbDisposed.ToString()); //////// ttime.Reset(); ////////} if (colleague.ShouldNotCall) { DebugMsg("_+_+_+_+ InvokeRecursively called on colleague that is disposed/disposing: " + colleague.ToString()); return false; // stop the processing } IxCoreColleague[] targets = colleague.GetMessageTargets(); // Try following the 'Code Performance' guidelines which says that // .."foreach introduces both managed heap and virtual function overhead.. // This can be a significant factor in performance-sensitive regions of your application." // This section of code is indeed a very performance-sensitive region! // for (int index = 0; index < targets.Length; index++) // foreach(IxCoreColleague target in targets) { if (!ProcessMessages) return true; IxCoreColleague target = targets[index]; if(target == null) { //Debug.WriteLine("Warning, target null."); continue; } //this section is the leaf of the search tree if (target == colleague) { //Check to see whether we have encountered this target before. //how can we encounter the same one twice? //This will happen when more than one colleague includes some shared object as one of its children. //in xWorks, this happens with the RecordClerk, which is not a top-level colleague on its own. // The following is logically equivalent to //if (previous.Contains(target)) //{ // break; //} //previous.Add(target); // but faster. int oldCount = previous.Count; previous.Add(target); if (oldCount == previous.Count) break; // it was already present, that is, we've processed it before. MethodInfo mi = CheckForMatchingMessage(colleague, methodName, parameterTypes); if (mi != null) { if (justCheckingForReceivers) { handled = true; break; } else { if (methodName == "OnMasterRefresh") { InvokeMethod(target, mi, parameterList); handled = true; } else { object o = InvokeMethod(target, mi, parameterList); handled = (o != null) ? (bool) o : false; } } } else { m_SavedCalls++; } } else //not at a leaf yet, keep going down the tree handled = InvokeRecursively(target, methodName, parameterTypes, parameterList, previous, stopWhenHandled, justCheckingForReceivers); if(handled && stopWhenHandled) { Trace.WriteLineIf(invokeSwitch.TraceVerbose, "-->handled=true And stopWhenHandled=true", invokeSwitch.DisplayName); break; } else if(handled) Trace.WriteLineIf(invokeSwitch.TraceVerbose, "-->handled=true", invokeSwitch.DisplayName); } // TraceVerboseLine("}"); // Trace.Unindent(); return handled; }
private object InvokeMethod(IxCoreColleague target, MethodInfo mi, object[] parameterList) { if (!ProcessMessages) return null; try { Trace.Indent(); if (invokeSwitch.TraceInfo) { if (parameterList.Length > 0 && parameterList[0] != null) Trace.WriteLine(" Invoking Method: " + mi.Name + "('" + parameterList[0].ToString() + "')" + " on " + target.ToString(), invokeSwitch.DisplayName); else Trace.WriteLine(" Invoking Method: " + mi.Name + " on " + target.ToString(), invokeSwitch.DisplayName); } #if false string objName = ""; objName = target.ToString() + target.GetHashCode().ToString(); // DLH TESTING - not finished yet... if (IsDisposedColleague(objName)) { Debug.WriteLine("##Not Invoking disposed object:"+objName); return null; } #endif /// ***************************************************************************** /// Have seen a stack situation that the Mediator has been disposed of after /// returning from this call - IOW's the following call allows re-entrance. /// That's why the exception follows to handle a known case when processing the /// ExitApplication msg. /// ***************************************************************************** object returnValue = mi.Invoke(target, parameterList); if (m_isDisposed) throw new DisposedInAnotherFrameException(); if (target == m_temporaryColleague && !mi.Name.StartsWith("OnDisplay")) { RemoveColleague(m_temporaryColleague); m_temporaryColleague = null; // only keep one temporary colleague at a time (menu based) } Trace.Unindent(); return returnValue; } //note that we don't want to catch just any kind of the exception here, //most exceptions will be invoked by the method that we actually called. //the only exceptions we want to catch are the ones that just mean that we failed to find //a suitable method. These we can report if in debug-mode, otherwise ignore. catch(System.ArgumentException error) { //I once spent close to an hour wondering what was causing the failure here. //The exception message was "Object type cannot be converted to target type." //the answer was that I had made the signature be a UIListDisplayProperties //when it should have been a UIItemDisplayProperties. The two can be pretty hard to //distinguish visually. (John Hatton) Debug.Fail("The method '"+mi.Name+"' was found but couldn't be invoked. Maybe has the wrong signature?", error.Message); } // catch(ConfigurationException error) // { // throw error; //already has good user notification message in it // } // catch(RuntimeConfigurationException error) // { // throw error; //already has good user notification message in it // } catch(TargetInvocationException error) { Exception inner = error.InnerException; //unwrap, for example, a ConfigurationException // See LT-1629 "Closing one db while opening another crashes". The following // two lines appear to fix this bug, although I'm not too happy about this // asynchronous behavior appearing where we (or at least I) don't expect it. // Unfortunately, that's inherent with message handling architecture when // handling one message allows other messages to be handled before it finishes. // - SteveMc if (inner is System.NullReferenceException && mi.Name == "OnChooseLangProject") { // We probably closed the target's window after choosing another project, // but before getting to this point in processing the ChooseLP message. So // ignore the exception. return null; } string s = "Something went wrong trying to invoke "+ target.ToString() +":"+ mi.Name +"()."; //if we just send on the error, then the caller //will find it more easy to trap particular kind of exceptions. On the other hand, //if the exception makes it all the way to the user, then we will really want to see this extra string (s) //at the top level. throw new Exception(s, inner); //if we were to just bring up the green box), then that makes it impossible for the caller to catch //the exception. In particular, the link-jumping column can fail if the object it is trying to jump to //has been deleted. This is really not an "error", and if we let the exception get back to the //jumping code, then it can notify the user more calmly. //SIL.Utils.ErrorReporter.ReportException(new ApplicationException(s, inner)); } return null; }
/// <summary> /// /// </summary> /// <param name="target"></param> /// <param name="methodName"></param> /// <param name="parameterTypes">Currently, use null here if you have a ref param</param> /// <param name="parameterList"></param> /// <returns>null or the MethodInfo if a matching one was found</returns> private MethodInfo CheckForMatchingMessage(IxCoreColleague target, string methodName, Type[] parameterTypes) { //NB: I did not think about these flags; I just copied them from an example BindingFlags flags = BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Instance; Type type = target.GetType(); MethodInfo mi; // // By using the JetBrains dotTrace profiler, the addition of m_TypeMethodInfo is saving // a significant (>20%) amount of time by not having to make as many calls to the expensive // System.Type.GetMethod() // if (parameterTypes == null) // callers currently must use null here if they have a "ref" param (TODO) { Dictionary<string, MethodInfo> methodDict; if (m_TypeMethodInfo.TryGetValue(type, out methodDict)) { if (methodDict.TryGetValue(methodName, out mi)) return mi; } else { methodDict = new Dictionary<string, MethodInfo>(); m_TypeMethodInfo[type] = methodDict; } mi = type.GetMethod(methodName, flags); methodDict[methodName] = mi; } else { var key = parameterTypes.Length + methodName; // method name could end with number, but not start. Dictionary<string, MethodInfo> methodDict; if (m_TypeMethodInfo.TryGetValue(type, out methodDict)) { if (methodDict.TryGetValue(key, out mi)) return mi; } else { methodDict = new Dictionary<string, MethodInfo>(); m_TypeMethodInfo[type] = methodDict; } mi = type.GetMethod(methodName, flags, null, parameterTypes, null); methodDict[key] = mi; } return mi; }
/// <summary> /// Constructor with paramters being required. /// </summary> /// <param name="mediator">Mediator that will handle the collegue during its temporary liketime.</param> /// <param name="temporaryColleague"></param> public TemporaryColleagueParameter(Mediator mediator, IxCoreColleague temporaryColleague, bool shouldDispose) { if (mediator == null) throw new ArgumentNullException("'mediator' parameter cannot be null."); if (temporaryColleague == null) throw new ArgumentNullException("'temporaryColleague' parameter cannot be null."); m_mediator = mediator; m_temporaryColleague = temporaryColleague; m_shouldDispose = shouldDispose; }
private bool HandleRightClickOnObject(int hvoReal, IxCoreColleague additionalTarget) { Debug.Assert(Mediator != null); CmObjectUi rightClickUiObj = CmObjectUi.MakeUi(Cache, hvoReal); if (rightClickUiObj != null) { rightClickUiObj.AdditionalColleague = additionalTarget; m_fHandlingRightClickMenu = true; try { //Debug.WriteLine("hvoReal=" + hvoReal.ToString() + " " + ui.Object.ShortName + " " + ui.Object.ToString()); return rightClickUiObj.HandleRightClick(Mediator, this, true, CmObjectUi.MarkCtrlClickItem); } finally { m_fHandlingRightClickMenu = false; rightClickUiObj.Dispose(); } } return false; }
/// <summary> /// /// </summary> /// <param name="target"></param> /// <param name="methodName"></param> /// <param name="parameterTypes">Currently, use null here if you have a ref param</param> /// <param name="parameterList"></param> /// <returns>null or the MethodInfo if a matching one was found</returns> private MethodInfo CheckForMatchingMessage(IxCoreColleague target, string methodName, Type[] parameterTypes, object[] parameterList, Set<int> previous) { #if false //tostring here is too expensive to leave lying around Trace.Indent(); TraceVerboseLine(" Checking : "+ target.ToString()); #endif int x = target.GetHashCode(); if (previous.Contains(x)) { throw new ArgumentException("XCore Mediator encountered the same " + target.ToString() + " twice on check for " + methodName + ", as if there is a loop."); } #if false TraceVerboseLine("Adding "+target.ToString()+":"+x.ToString()); #endif previous.Add(x); //NB: I did not think about these flags; I just copied them from an example BindingFlags flags = BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Instance; Type type = target.GetType(); MethodInfo mi; // // By using the JetBrains dotTrace profiler, the addition of m_TypeMethodInfo is saving // a significant (>20%) amount of time by not having to make as many calls to the expensive // System.Type.GetMethod() // if (parameterTypes == null) // callers currently must use null here if they have a "ref" param (TODO) { string key = type.ToString() + "_" + methodName; if (m_TypeMethodInfo.ContainsKey(key)) { mi = m_TypeMethodInfo[key]; } else { mi = type.GetMethod(methodName, flags); m_TypeMethodInfo[key] = mi; } } else { string key2 = type.ToString() + "_" + methodName + "_" + parameterTypes.Length.ToString(); if (m_TypeMethodInfo.ContainsKey(key2)) { mi = m_TypeMethodInfo[key2]; } else { mi= type.GetMethod(methodName, flags, null, parameterTypes, null); m_TypeMethodInfo[key2] = mi; } } #if false Trace.Unindent(); #endif return mi; }
private bool ColleagueHasBeenDisposed(IxCoreColleague target) { bool hasBeenDisposed = false; if (target is IDisposable) { // Have to use reflection to see if it has an IsDisposed property, // since that is not part of the interface for IDisposable for some odd reason. // Get the property infor for the ActiveViewHelper property on the control // (assuming there is one). PropertyInfo propInfo = target.GetType().GetProperty("IsDisposed", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); // If we successfully found an ActiveViewHelper property, then reference // it to set our class' active view helper member variable. if (propInfo != null) { hasBeenDisposed = (bool)propInfo.GetValue(target, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, null, null); if (!hasBeenDisposed) { propInfo = target.GetType().GetProperty("Disposing", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); // If we successfully found an ActiveViewHelper property, then reference // it to set our class' active view helper member variable. if (propInfo != null) { hasBeenDisposed = (bool)propInfo.GetValue(target, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, null, null); } } } } return hasBeenDisposed; }