[reflection] Coop handles icalls in System.Reflection and System.RuntimeTypeHandle...
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Base / Core / ContextItemManager.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation 
5 {
6
7     using System;
8     using System.Collections;
9     using System.Collections.Generic;
10     using System.Collections.ObjectModel;
11     using System.Diagnostics.CodeAnalysis;
12     using System.Activities.Presentation;
13
14     using System.Runtime;
15
16     // <summary>
17     // The ContextItemManager class maintains a set of context items.  A context
18     // item represents a piece of transient state in a designer.
19     //
20     // ContextItems must define an empty constructor.  This empty constructor
21     // version of a context item represents its default value, and will be the
22     // value returned from GetItem if the context item manager does not contain
23     // a context item of the requested type.
24     //
25     // The ContextItemManager supports context layers.  A context layer is a
26     // separation in the set of context items and is useful when providing modal
27     // functions.  For example, when switching modes in the designer to show the
28     // tab order layout it may be desirable to disable adding items from the
29     // toolbox and change the user mouse and keyboard gestures to focus on setting
30     // the tab order.  Rather than grabbing and storing context items before
31     // replacing them with new values, a developer can simply call CreateLayer.
32     // Once the layer is created, all subsequent context changes go to that layer.
33     //
34     // When the developer is done with the layer, as would be the case when a user
35     // switches out of tab order mode, she simply calls Remove on the layer. This
36     // removes all context items that were added to the layer and restores the context
37     // to its previous set of values before the layer was created.
38     // </summary>
39     [SuppressMessage(FxCop.Category.Naming, FxCop.Rule.IdentifiersShouldHaveCorrectSuffix)]
40     public abstract class ContextItemManager : IEnumerable<ContextItem>
41     {
42
43         // <summary>
44         // Creates a new ContextItemManager object.
45         // </summary>
46         protected ContextItemManager() 
47         {
48         }
49
50         // <summary>
51         // Returns true if the item manager contains an item of the given type.
52         // </summary>
53         // <param name="itemType">The type of item to check.</param>
54         // <returns>True if the context contains an instance of this item type.</returns>
55         // <exception cref="ArgumentNullException">if itemType is null.</exception>
56         public abstract bool Contains(Type itemType);
57
58         // <summary>
59         // Returns true if the item manager contains an item of the given type.
60         // </summary>
61         // <typeparam name="TItemType">The type of item to check.</typeparam>
62         // <returns>True if the context contains an instance of this item type.</returns>
63         [SuppressMessage(FxCop.Category.Design, FxCop.Rule.GenericMethodsShouldProvideTypeParameter)]
64         public bool Contains<TItemType>() where TItemType : ContextItem
65         {
66             return Contains(typeof(TItemType));
67         }
68
69         // <summary>
70         // Enumerates the context items in the editing context.  This enumeration
71         // includes prior layers unless the enumerator hits an isolated layer.
72         // Enumeration is typically not useful in most scenarios but it is provided so
73         // that developers can search in the context and learn what is placed in it.
74         // </summary>
75         // <returns>An enumeration of context items.</returns>
76         public abstract IEnumerator<ContextItem> GetEnumerator();
77
78         // <summary>
79         // Returns an instance of the requested item type.  If there is no context
80         // item with the given type, an empty item will be created.
81         // </summary>
82         // <param name="itemType">The type of item to return.</param>
83         // <returns>A context item of the requested type.  If there is no item in the context of this type a default one will be created.</returns>
84         // <exception cref="ArgumentNullException">if itemType is null.</exception>
85         public abstract ContextItem GetValue(Type itemType);
86
87         // <summary>
88         // Returns an instance of the requested item type.  If there is no context
89         // item with the given type, an empty item will be created.
90         // </summary>
91         // <typeparam name="TItemType">The type of item to return.</typeparam>
92         // <returns>A context item of the requested type.  If there is no item in the context of this type a default one will be created.</returns>
93         [SuppressMessage(FxCop.Category.Design, FxCop.Rule.GenericMethodsShouldProvideTypeParameter)]
94         public TItemType GetValue<TItemType>() where TItemType : ContextItem
95         {
96             return (TItemType)GetValue(typeof(TItemType));
97         }
98
99         // <summary>
100         // This is a helper method that invokes the protected OnItemChanged
101         // method on ContextItem.
102         // </summary>
103         // <param name="context">The editing context in use.</param>
104         // <param name="item">The new context item.</param>
105         // <param name="previousItem">The previous context item.</param>
106         // <exception cref="ArgumentNullException">if context, item or previousItem is null.</exception>
107         protected static void NotifyItemChanged(EditingContext context, ContextItem item, ContextItem previousItem) 
108         {
109             if (context == null) 
110             {
111                 throw FxTrace.Exception.ArgumentNull("context");
112             }
113             if (item == null) 
114             {
115                 throw FxTrace.Exception.ArgumentNull("item");
116             }
117             if (previousItem == null) 
118             {
119                 throw FxTrace.Exception.ArgumentNull("previousItem");
120             }
121             item.InvokeOnItemChanged(context, previousItem);
122         }
123
124         // <summary>
125         // This sets a context item to the given value.  It is illegal to pass
126         // null here.  If you want to set a context item to its empty value create
127         // an instance of the item using a default constructor.
128         // </summary>
129         // <param name="value">The value to set into the context item manager.</param>
130         public abstract void SetValue(ContextItem value);
131
132         // <summary>
133         // Adds an event callback that will be invoked with a context item of the given item type changes.
134         // </summary>
135         // <param name="contextItemType">The type of item you wish to subscribe to.</param>
136         // <param name="callback">A callback that will be invoked when contextItemType changes.</param>
137         // <exception cref="ArgumentNullException">if contextItemType or callback is null.</exception>
138         public abstract void Subscribe(Type contextItemType, SubscribeContextCallback callback);
139
140         // <summary>
141         // Adds an event callback that will be invoked with a context item of the given item type changes.
142         // </summary>
143         // <typeparam name="TContextItemType">The type of item you wish to subscribe to.</typeparam>
144         // <param name="callback">A callback that will be invoked when contextItemType changes.</param>
145         // <exception cref="ArgumentNullException">if callback is null.</exception>
146         [SuppressMessage(FxCop.Category.Design, FxCop.Rule.GenericMethodsShouldProvideTypeParameter)]
147         public void Subscribe<TContextItemType>(SubscribeContextCallback<TContextItemType> callback) where TContextItemType : ContextItem
148         {
149             if (callback == null) 
150             {
151                 throw FxTrace.Exception.ArgumentNull("callback");
152             }
153             SubscribeProxy<TContextItemType> proxy = new SubscribeProxy<TContextItemType>(callback);
154             Subscribe(typeof(TContextItemType), proxy.Callback);
155         }
156
157         // <summary>
158         //     Removes a subscription.
159         // </summary>
160         // <typeparam name="TContextItemType">The type of context item to remove the callback from.</typeparam>
161         // <param name="callback">The callback to remove.</param>
162         // <exception cref="ArgumentNullException">if callback is null.</exception>
163         [SuppressMessage(FxCop.Category.Design, FxCop.Rule.GenericMethodsShouldProvideTypeParameter)]
164         public void Unsubscribe<TContextItemType>(SubscribeContextCallback<TContextItemType> callback) where TContextItemType : ContextItem
165         {
166             if (callback == null) 
167             {
168                 throw FxTrace.Exception.ArgumentNull("callback");
169             }
170             SubscribeProxy<TContextItemType> proxy = new SubscribeProxy<TContextItemType>(callback);
171             Unsubscribe(typeof(TContextItemType), proxy.Callback);
172         }
173
174         // <summary>
175         //     Removes a subscription.
176         // </summary>
177         // <param name="contextItemType">The type of context item to remove the callback from.</param>
178         // <param name="callback">The callback to remove.</param>
179         // <exception cref="ArgumentNullException">if contextItemType or callback is null.</exception>
180         public abstract void Unsubscribe(Type contextItemType, SubscribeContextCallback callback);
181
182         // <summary>
183         //     This is a helper method that returns the target object for a delegate.
184         //     If the delegate was created to proxy a generic delegate, this will correctly
185         //     return the original object, not the proxy.
186         // </summary>
187         // <param name="callback">The callback whose target you want.</param>
188         // <exception cref="ArgumentNullException">if callback is null.</exception>
189         // <returns>The target object of the callback.</returns>
190         protected static object GetTarget(Delegate callback) 
191         {
192             if (callback == null) 
193             {
194                 throw FxTrace.Exception.ArgumentNull("callback");
195             }
196
197             ICallbackProxy proxy = callback.Target as ICallbackProxy;
198             if (proxy != null) 
199             {
200                 return proxy.OriginalTarget;
201             }
202
203             return callback.Target;
204         }
205
206         // <summary>
207         //     This is a helper method that performs a Delegate.Remove, but knows
208         //     how to unwrap delegates that are proxies to generic callbacks.  Use
209         //     this in your Unsubscribe implementations.
210         // </summary>
211         // <param name="existing">The existing delegate.</param>
212         // <param name="toRemove">The delegate to be removed from existing.</param>
213         // <returns>The new delegate that should be assigned to existing.</returns>
214         protected static Delegate RemoveCallback(Delegate existing, Delegate toRemove) 
215         {
216             if (existing == null) 
217             {
218                 return null;
219             }
220             if (toRemove == null) 
221             {
222                 return existing;
223             }
224
225             ICallbackProxy toRemoveProxy = toRemove.Target as ICallbackProxy;
226             if (toRemoveProxy == null) 
227             {
228                 // The item to be removed is a normal delegate.  Just call
229                 // Delegate.Remove
230                 return Delegate.Remove(existing, toRemove);
231             }
232
233             toRemove = toRemoveProxy.OriginalDelegate;
234
235             Delegate[] invocationList = existing.GetInvocationList();
236             bool removedItems = false;
237
238             for (int idx = 0; idx < invocationList.Length; idx++) 
239             {
240                 Delegate item = invocationList[idx];
241                 ICallbackProxy itemProxy = item.Target as ICallbackProxy;
242                 if (itemProxy != null) 
243                 {
244                     item = itemProxy.OriginalDelegate;
245                 }
246
247                 if (item.Equals(toRemove)) 
248                 {
249                     invocationList[idx] = null;
250                     removedItems = true;
251                 }
252             }
253
254             if (removedItems) 
255             {
256                 // We must create a new delegate containing the 
257                 // invocation list that is is left
258                 existing = null;
259                 foreach (Delegate d in invocationList) 
260                 {
261                     if (d != null) 
262                     {
263                         if (existing == null) 
264                         {
265                             existing = d;
266                         }
267                         else 
268                         {
269                             existing = Delegate.Combine(existing, d);
270                         }
271                     }
272                 }
273             }
274
275             return existing;
276         }
277
278         // <summary>
279         // Implementation of default IEnumerable.
280         // </summary>
281         IEnumerator IEnumerable.GetEnumerator() 
282         {
283             return GetEnumerator();
284         }
285
286         private interface ICallbackProxy 
287         {
288             Delegate OriginalDelegate 
289             { get; }
290             object OriginalTarget 
291             { get; }
292         }
293
294         // <summary>
295         // This is a simple proxy that converts a non-generic subscribe callback to a generic
296         // one.
297         // </summary>
298         // <typeparam name="TContextItemType"></typeparam>
299         private class SubscribeProxy<TContextItemType> : ICallbackProxy where TContextItemType : ContextItem 
300         {
301             private SubscribeContextCallback<TContextItemType> _genericCallback;
302
303             internal SubscribeProxy(SubscribeContextCallback<TContextItemType> callback) 
304             {
305                 _genericCallback = callback;
306             }
307
308             internal SubscribeContextCallback Callback 
309             {
310                 get {
311                     return new SubscribeContextCallback(SubscribeContext);
312                 }
313             }
314
315             Delegate ICallbackProxy.OriginalDelegate 
316             {
317                 get { return _genericCallback; }
318             }
319
320             object ICallbackProxy.OriginalTarget 
321             {
322                 get {
323                     return _genericCallback.Target;
324                 }
325             }
326
327             private void SubscribeContext(ContextItem item) 
328             {
329                 if (item == null) 
330                 {
331                     throw FxTrace.Exception.ArgumentNull("item");
332                 }
333                 _genericCallback((TContextItemType)item);
334             }
335         }
336     }
337 }