Fix operator != handling of LHS null
[mono.git] / mcs / class / System.Runtime.Caching / System.Runtime.Caching / ChangeMonitor.cs
1 //
2 // ChangeMonitor.cs
3 //
4 // Authors:
5 //      Marek Habersack <mhabersack@novell.com>
6 //
7 // Copyright (C) 2010 Novell, Inc. (http://novell.com/)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29
30 namespace System.Runtime.Caching
31 {
32         public abstract class ChangeMonitor : IDisposable
33         {
34                 bool initializationComplete;
35                 bool notifyOnChangedCalled;
36                 OnChangedCallback onChangedCallback;
37                 object onChangedState;
38
39                 public bool HasChanged { get; private set; }
40                 public bool IsDisposed { get; private set; }
41                 public abstract string UniqueId { get; }
42                 
43                 protected ChangeMonitor ()
44                 {
45                 }
46
47                 public void Dispose ()
48                 {
49                         if (IsDisposed)
50                                 return;
51
52                         if (!initializationComplete)
53                                 // TODO: check if Dispose (bool) is called in this case
54                                 throw new InvalidOperationException ("Initialization is not complete in the derived change-monitor class that called the base Dispose method.");
55
56                         try {
57                                 try {
58                                         InvokeOnChangedCallback (onChangedState);
59                                 } catch {
60                                         // ignore
61                                         // TODO: check what happens if the callback throws an exception - is
62                                         // Dispose (true) called then?
63                                 }
64                 
65                                 Dispose (true);
66                         } finally {     
67                                 IsDisposed = true;
68                         }
69                 }
70
71                 protected abstract void Dispose (bool disposing);
72
73                 protected void InitializationComplete ()
74                 {
75                         initializationComplete = true;
76                         if (HasChanged)
77                                 Dispose ();
78                 }
79
80                 void InvokeOnChangedCallback (object state)
81                 {
82                         if (onChangedCallback == null)
83                                 return;
84
85                         try {
86                                 onChangedCallback (state);
87                         } finally {
88                                 onChangedCallback = null;
89                                 onChangedState = null;
90                         }
91                 }
92                 
93                 public void NotifyOnChanged (OnChangedCallback onChangedCallback)
94                 {
95                         if (onChangedCallback == null)
96                                 throw new ArgumentNullException ("onChangedCallback");
97
98                         if (notifyOnChangedCalled)
99                                 throw new InvalidOperationException ("The callback method has already been invoked.");
100
101                         notifyOnChangedCalled = true;
102                         this.onChangedCallback = onChangedCallback;
103                         if (HasChanged) {
104                                 InvokeOnChangedCallback (onChangedState);
105                                 return;
106                         }
107                         
108                 }
109
110                 protected void OnChanged (object state)
111                 {
112                         HasChanged = true;
113                         try {
114                                 if (onChangedCallback == null)
115                                         onChangedState = state;
116                                 else
117                                         InvokeOnChangedCallback (state);
118                         } catch {
119                                 // ignore
120                                 // TODO: check what happens if callback throws an exception - is
121                                 // Dispose below called?
122                         } finally {
123                                 if (initializationComplete)
124                                         Dispose ();
125                         }
126                 }
127         }
128 }