[corlib] Update ValueTuple implementation
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / ObjectReferenceService.cs
1 // <copyright>
2 //   Copyright (c) Microsoft Corporation.  All rights reserved.
3 // </copyright>
4
5 namespace System.Activities.Presentation
6 {
7     using System.Activities.Debugger;
8     using System.Activities.Presentation.Model;
9     using System.Activities.Presentation.Services;
10     using System.Activities.Presentation.Validation;
11     using System.Activities.Presentation.Xaml;
12     using System.Collections.Generic;
13     using System.Runtime;
14
15     /// <summary>
16     /// This interface is used by visual studio integration to acquire a AppDomain serialization friendly reference to an object.
17     /// </summary>
18     public sealed class ObjectReferenceService
19     {
20         private Dictionary<Guid, object> objectReferenceIds;
21         private Dictionary<Guid, int> objectReferenceCount;
22         private HashSet<Guid> subscribedForSourceLocationChanges;
23         private EditingContext context;
24         private ModelSearchServiceImpl modelSearchService;
25         private ModelTreeManager modelTreeManager;
26
27         /// <summary>
28         /// This interface is used by visual studio integration to acquire a AppDomain serialization friendly reference to an object.
29         /// </summary>
30         /// <param name="context">The EditingContext of the current WorkflowDesigner.</param>
31         public ObjectReferenceService(EditingContext context)
32         {
33             if (context == null)
34             {
35                 throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
36             }
37
38             this.context = context;
39             this.context.Services.Subscribe<ModelSearchService>(new SubscribeServiceCallback<ModelSearchService>(this.OnModelSearchServiceAvailable));
40             this.context.Services.Subscribe<ModelTreeManager>(new SubscribeServiceCallback<ModelTreeManager>(this.OnModelTreeManagerAvailable));
41         }
42
43         /// <summary>
44         /// Fire an event when the SourceLocation of an ObjectReference might be updated because of a Save operation.
45         /// </summary>
46         public event EventHandler<SourceLocationUpdatedEventArgs> SourceLocationUpdated;
47
48         private Dictionary<Guid, object> ObjectReferenceIds
49         {
50             get
51             {
52                 if (this.objectReferenceIds == null)
53                 {
54                     this.objectReferenceIds = new Dictionary<Guid, object>();
55                 }
56
57                 return this.objectReferenceIds;
58             }
59         }
60
61         private Dictionary<Guid, int> ObjectReferenceCount
62         {
63             get
64             {
65                 if (this.objectReferenceCount == null)
66                 {
67                     this.objectReferenceCount = new Dictionary<Guid, int>();
68                 }
69
70                 return this.objectReferenceCount;
71             }
72         }
73
74         private HashSet<Guid> SubscribedForSourceLocationChanges
75         {
76             get
77             {
78                 if (this.subscribedForSourceLocationChanges == null)
79                 {
80                     this.subscribedForSourceLocationChanges = new HashSet<Guid>();
81                 }
82
83                 return this.subscribedForSourceLocationChanges;
84             }
85         }
86
87         /// <summary>
88         /// Acquire a reference by the SourceLocation of the object.
89         /// Notice this method will automatically register the object to listen to SourceLocationUpdated, if available.
90         /// </summary>
91         /// <param name="startLine">The start line of the object.</param>
92         /// <param name="startColumn">The start column of the object.</param>
93         /// <param name="endLine">The end line of the object.</param>
94         /// <param name="endColumn">The end column of the object.</param>
95         /// <returns>The object reference.</returns>
96         public Guid AcquireObjectReference(int startLine, int startColumn, int endLine, int endColumn)
97         {
98             if (this.modelSearchService != null)
99             {
100                 ModelItem modelItem = this.modelSearchService.FindModelItem(startLine, startColumn, endLine, endColumn);
101                 if (modelItem != null)
102                 {
103                     object searchObject = modelItem.GetCurrentValue();
104                     Guid result = this.AcquireObjectReference(searchObject);
105                     this.SubscribedForSourceLocationChanges.Add(result);
106                     return result;
107                 }
108             }
109
110             return Guid.Empty;
111         }
112
113         /// <summary>
114         /// Acquire a reference of an object by its actual reference.
115         /// </summary>
116         /// <param name="obj">The object which we need to acquire a reference for.</param>
117         /// <returns>The object reference.</returns>
118         public Guid AcquireObjectReference(object obj)
119         {
120             if (obj == null)
121             {
122                 throw FxTrace.Exception.AsError(new ArgumentNullException("obj"));
123             }
124
125             bool found = false;
126             Guid objectReferenceId = Guid.NewGuid();
127             foreach (KeyValuePair<Guid, object> kvp in this.ObjectReferenceIds)
128             {
129                 if (object.ReferenceEquals(kvp.Value, obj))
130                 {
131                     objectReferenceId = kvp.Key;
132                     found = true;
133                     break;
134                 }
135             }
136
137             if (!found)
138             {
139                 this.ObjectReferenceIds.Add(objectReferenceId, obj);
140             }
141
142             this.IncreaseReferenceCount(objectReferenceId);
143             return objectReferenceId;
144         }
145
146         /// <summary>
147         /// Release the activity reference - this allow the designer infrastructure to release the actual reference to the activity - thus avoiding memory leak.
148         /// </summary>
149         /// <param name="objectReferenceId">The activity reference.</param>
150         public void ReleaseObjectReference(Guid objectReferenceId)
151         {
152             if (this.DecreaseReferenceCount(objectReferenceId))
153             {
154                 this.ObjectReferenceIds.Remove(objectReferenceId);
155                 if (this.SubscribedForSourceLocationChanges.Contains(objectReferenceId))
156                 {
157                     this.SubscribedForSourceLocationChanges.Remove(objectReferenceId);
158                 }
159             }
160         }
161
162         /// <summary>
163         /// Obtain the actual reference to the object by its ObjectReference - this method should be called within the designer AppDomain only.
164         /// </summary>
165         /// <param name="objectReferenceId">The activity reference.</param>
166         /// <param name="obj">The de-referenced activity, if the reference is available, or null otherwise.</param>
167         /// <returns>True if the activity reference can be successfully de-referenced.</returns>
168         public bool TryGetObject(Guid objectReferenceId, out object obj)
169         {
170             return this.ObjectReferenceIds.TryGetValue(objectReferenceId, out obj);
171         }
172
173         internal void OnSaveCompleted()
174         {
175             if (this.SourceLocationUpdated != null)
176             {
177                 if (this.modelSearchService != null)
178                 {
179                     foreach (Guid subscribedObjectReference in this.SubscribedForSourceLocationChanges)
180                     {
181                         object subscribedObject = this.ObjectReferenceIds[subscribedObjectReference];
182                         SourceLocation updatedSourceLocation = this.modelSearchService.FindSourceLocation(this.modelTreeManager.GetModelItem(subscribedObject));
183                         if (updatedSourceLocation != null)
184                         {
185                             this.SourceLocationUpdated(null, new SourceLocationUpdatedEventArgs(subscribedObjectReference, updatedSourceLocation));
186                         }
187                     }
188                 }
189             }
190         }
191
192         private void OnModelSearchServiceAvailable(ModelSearchService modelSearchService)
193         {
194             if (modelSearchService != null)
195             {
196                 this.modelSearchService = modelSearchService as ModelSearchServiceImpl;
197             }
198         }
199
200         private void OnModelTreeManagerAvailable(ModelTreeManager modelTreeManager)
201         {
202             if (modelTreeManager != null)
203             {
204                 this.modelTreeManager = modelTreeManager as ModelTreeManager;
205             }
206         }
207
208         private void IncreaseReferenceCount(Guid objectReferenceId)
209         {
210             int referenceCount;
211             if (!this.ObjectReferenceCount.TryGetValue(objectReferenceId, out referenceCount))
212             {
213                 referenceCount = 0;
214             }
215
216             referenceCount++;
217             this.ObjectReferenceCount[objectReferenceId] = referenceCount;
218         }
219
220         private bool DecreaseReferenceCount(Guid objectReferenceId)
221         {
222             Fx.Assert(this.ObjectReferenceCount.ContainsKey(objectReferenceId), "DecreaseReferenceCount should not be called when there is no reference.");
223             if (this.ObjectReferenceCount.ContainsKey(objectReferenceId))
224             {
225                 int referenceCount = this.ObjectReferenceCount[objectReferenceId] - 1;
226                 if (referenceCount == 0)
227                 {
228                     this.ObjectReferenceCount.Remove(objectReferenceId);
229                     return true;
230                 }
231                 else
232                 {
233                     this.ObjectReferenceCount[objectReferenceId] = referenceCount;
234                     return false;
235                 }
236             }
237
238             return true;
239         }
240     }
241 }