File: RTTrackingProfile.cs

package info (click to toggle)
mono 6.14.1%2Bds2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,282,732 kB
  • sloc: cs: 11,182,461; xml: 2,850,281; ansic: 699,123; cpp: 122,919; perl: 58,604; javascript: 30,841; asm: 21,845; makefile: 19,602; sh: 10,973; python: 4,772; pascal: 925; sql: 859; sed: 16; php: 1
file content (748 lines) | stat: -rw-r--r-- 30,265 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.IO;
using System.Reflection;
using System.Diagnostics;
using System.Runtime.Serialization;
using System.Security.Permissions;
using System.Globalization;

//using System.Workflow.Activities;
using System.Workflow.ComponentModel;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;
using Hosting = System.Workflow.Runtime.Hosting;
using System.Workflow.Runtime.Tracking;


namespace System.Workflow.Runtime
{
    /// <summary>
    /// RTTrackingProfile contains functionality specific to the runtime such as 
    /// trackpoint and location matching and caching, cloning, handling dynamic updates...
    /// </summary>
    internal class RTTrackingProfile : ICloneable // ICloneable is deprecated
    {
        #region Private Data Members
        //
        // Client defined profile
        private TrackingProfile _profile = null;
        //
        // Type of the workflow that this profile is associated to
        private Type _workflowType = null;
        private Type _serviceType = null;
        //
        // List of qualified ids and the trackpoints that declared themselves as matches during static examination
        private Dictionary<string, List<ActivityTrackPointCacheItem>> _activities = new Dictionary<string, List<ActivityTrackPointCacheItem>>();
        private List<string> _activitiesIgnore = new List<string>();

        private Dictionary<string, List<UserTrackPoint>> _user = new Dictionary<string, List<UserTrackPoint>>();
        private List<string> _userIgnore = new List<string>();
        //
        // Indicates that the RTTrackingProfile instance is private and is safe to modify for a specific instance
        private bool _isPrivate = false;
        //
        // Indicates if a dynamic update is in-flight
        private bool _pendingWorkflowChange = false;
        //
        // The changes for a dynamic update
        private IList<WorkflowChangeAction> _pendingChanges = null;
        //
        // Activities (including those that are being added) can start executing while a dynamic update is pending
        // These cannot be added to the main cache until the update succeeds because the update might roll back.
        // However since we have to search for matching track points we might as well save that work. 
        // This list will be copied into the main cache if the dynamic update completes successfully
        private Dictionary<string, List<ActivityTrackPointCacheItem>> _dynamicActivities = null;
        private List<string> _dynamicActivitiesIgnore = null;

        private Dictionary<string, List<UserTrackPoint>> _dynamicUser = null;
        private List<string> _dynamicUserIgnore = null;

        #endregion

        #region Constructors
        /// <summary>
        /// Default constructor
        /// </summary>
        protected RTTrackingProfile()
        {
        }
        /// <summary>
        /// Primary constructor
        /// </summary>
        /// <param name="root"></param>
        /// <param name="workflowType"></param>
        /// <param name="profile"></param>
        internal RTTrackingProfile(TrackingProfile profile, Activity root, Type serviceType)
        {
            if (null == profile)
                throw new ArgumentNullException("profile");
            if (null == root)
                throw new ArgumentNullException("root");
            if (null == serviceType)
                throw new ArgumentNullException("serviceType");

            _workflowType = root.GetType();
            _serviceType = serviceType;
            //
            // "Clone" a private copy in case the tracking service holds a reference to 
            // the profile it gave us and attempts to modify it at a later point
            TrackingProfileSerializer tps = new TrackingProfileSerializer();

            StringWriter writer = new StringWriter(System.Globalization.CultureInfo.InvariantCulture);
            StringReader reader = null;

            TrackingProfile privateProfile = null;

            try
            {
                //
                // Let exceptions bubble back to the tracking service - 
                // the profile must be valid per the schema.
                tps.Serialize(writer, profile);
                reader = new StringReader(writer.ToString());
                privateProfile = tps.Deserialize(reader);
            }
            finally
            {
                if (null != reader)
                    reader.Close();

                if (null != writer)
                    writer.Close();
            }
            _profile = privateProfile;

            CheckAllActivities((Activity)root);
        }

        /// <summary>
        /// Constructor used for cloning.  
        /// </summary>
        /// <param name="runtimeProfile">RTTrackingProfile to clone</param>
        /// <remarks>All members are shallow copied!  Use MakePrivate to deep copy after cloning.</remarks>
        private RTTrackingProfile(RTTrackingProfile runtimeProfile)
        {
            //
            // Shallow copy
            _profile = runtimeProfile._profile;
            _isPrivate = runtimeProfile._isPrivate;
            _pendingChanges = runtimeProfile._pendingChanges;
            _pendingWorkflowChange = runtimeProfile._pendingWorkflowChange;
            _workflowType = runtimeProfile._workflowType;
            //
            // Deep copy the cache.  Items in the cache can
            // be shared but the cache themselves cannot as they may be modified
            //
            // Activity match and ignore cache
            _activities = new Dictionary<string, List<ActivityTrackPointCacheItem>>(runtimeProfile._activities.Count);
            foreach (KeyValuePair<string, List<ActivityTrackPointCacheItem>> kvp in runtimeProfile._activities)
                _activities.Add(kvp.Key, runtimeProfile._activities[kvp.Key]);

            _activitiesIgnore = new List<string>(runtimeProfile._activitiesIgnore);
            //
            // Pending dynamic update activity match and ignore cache
            if (null != runtimeProfile._dynamicActivities)
            {
                _dynamicActivities = new Dictionary<string, List<ActivityTrackPointCacheItem>>(runtimeProfile._dynamicActivities.Count);
                foreach (KeyValuePair<string, List<ActivityTrackPointCacheItem>> kvp in runtimeProfile._dynamicActivities)
                    _dynamicActivities.Add(kvp.Key, runtimeProfile._dynamicActivities[kvp.Key]);
            }

            if (null != runtimeProfile._dynamicActivitiesIgnore)
                _dynamicActivitiesIgnore = new List<string>(runtimeProfile._dynamicActivitiesIgnore);
            //
            // User event match and ignore cache
            _user = new Dictionary<string, List<UserTrackPoint>>(runtimeProfile._user.Count);
            foreach (KeyValuePair<string, List<UserTrackPoint>> kvp in runtimeProfile._user)
                _user.Add(kvp.Key, runtimeProfile._user[kvp.Key]);

            _userIgnore = new List<string>(runtimeProfile._userIgnore);
            //
            // Pending dynamic update activity match and ignore cache
            if (null != runtimeProfile._dynamicUser)
            {
                _dynamicUser = new Dictionary<string, List<UserTrackPoint>>(runtimeProfile._dynamicUser.Count);
                foreach (KeyValuePair<string, List<UserTrackPoint>> kvp in runtimeProfile._dynamicUser)
                    _dynamicUser.Add(kvp.Key, kvp.Value);
            }

            if (null != runtimeProfile._dynamicUserIgnore)
                _dynamicUserIgnore = new List<string>(runtimeProfile._dynamicUserIgnore);
        }

        #endregion

        #region Properties
        /// <summary>
        /// Indicates if the profile is specific to an individual instance.
        /// </summary>
        internal bool IsPrivate
        {
            get { return _isPrivate; }
            set
            {
                if (!(value) && (_isPrivate))
                    throw new InvalidOperationException(ExecutionStringManager.CannotResetIsPrivate);

                _isPrivate = value;
            }
        }
        /// <summary>
        /// Type of workflow to which this profile is associated
        /// </summary>
        internal Type WorkflowType
        {
            get { return _workflowType; }
        }

        /// <summary>
        /// Version of the profile
        /// </summary>
        internal Version Version
        {
            get { return _profile.Version; }
        }

        #endregion

        #region Internal Methods for Listeners

        internal bool TryTrackActivityEvent(Activity activity, ActivityExecutionStatus status, IServiceProvider provider, ActivityTrackingRecord record)
        {
            List<ActivityTrackPointCacheItem> points;
            //
            // Check the match caches.
            if (TryGetCacheItems(activity, out points))
            {
                bool ret = false;
                foreach (ActivityTrackPointCacheItem item in points)
                {
                    if (item.HasLocationConditions)
                    {
                        if (!item.Point.IsMatch(activity, status))
                            continue;
                    }

                    if (item.Events.Contains(status))
                    {
                        ret = true;
                        item.Point.Track(activity, provider, record.Body);
                        record.Annotations.AddRange(item.Point.Annotations);
                    }
                }
                return ret;
            }
            return false;
        }

        internal bool TryTrackUserEvent(Activity activity, string keyName, object argument, WorkflowExecutor exec, UserTrackingRecord record)
        {
            List<UserTrackPoint> points;
            if (TryGetCacheItems(activity, out points))
            {
                bool ret = false;
                foreach (UserTrackPoint point in points)
                {
                    if (point.IsMatch(activity, keyName, argument))
                    {
                        ret = true;
                        point.Track(activity, argument, exec, record.Body);
                        record.Annotations.AddRange(point.Annotations);
                    }
                }
                return ret;
            }
            return false;
        }

        internal bool TryTrackInstanceEvent(TrackingWorkflowEvent status, WorkflowTrackingRecord record)
        {
            bool track = false;
            foreach (WorkflowTrackPoint point in _profile.WorkflowTrackPoints)
            {
                if (point.IsMatch(status))
                {
                    record.Annotations.AddRange(point.Annotations);
                    track = true;
                }
            }
            return track;
        }


        /// <summary>
        /// Called by TrackingListener to determine if a subscription is needed for an activity.
        /// Also used as an entry point for dynamically building cache entries for dynamically added activities.  
        /// </summary>
        /// <param name="activity"></param>
        /// <param name="exec"></param>
        /// <returns></returns>
        internal bool ActivitySubscriptionNeeded(Activity activity)
        {
            List<ActivityTrackPointCacheItem> points = null;
            if ((!_pendingWorkflowChange) || ((_pendingWorkflowChange) && (!IsPendingUpdateActivity(activity, true))))
            {
                //
                // A dynamic update is not in progress or 
                // the activity is not part of the dynamic update.
                // The main cache has all matching track points
                //
                // 
                bool retry = true;
                while (retry)
                {
                    if (_activitiesIgnore.Contains(activity.QualifiedName))
                        return false;

                    if (_activities.TryGetValue(activity.QualifiedName, out points))
                        return true;
                    else
                        //
                        // This activity isn't in either cache, look it up in the profile and add to cache
                        CheckActivity(activity);
                }
                return false;
            }
            else
            {
                //
                // Dynamic update is in progress and this activity is being added as part of the update
                // Search the profile for matching track points and add them to the dynamic cache
                // (copied to the main cache at the successful completion of the update)
                // Don't go through CheckActivity because that adds to the main cache
                List<UserTrackPoint> user = null;
                if (CreateCacheItems(activity, out user))
                    CacheInsertUpdatePending(activity.QualifiedName, user);
                else
                    _dynamicUserIgnore.Add(activity.QualifiedName);

                if (CreateCacheItems(activity, out points))
                {
                    CacheInsertUpdatePending(activity.QualifiedName, points);
                    return true;
                }
                else
                {
                    _dynamicActivitiesIgnore.Add(activity.QualifiedName);
                    return false;
                }
            }
        }

        public void WorkflowChangeBegin(IList<WorkflowChangeAction> changeActions)
        {
            Debug.Assert(!_pendingWorkflowChange, "_pendingWorkflowChange should be false.");
            if (_pendingWorkflowChange)
                throw new InvalidOperationException(ExecutionStringManager.DynamicUpdateIsNotPending);

            if (!_isPrivate)
                throw new InvalidOperationException(ExecutionStringManager.ProfileIsNotPrivate);
            //
            // Initialize the temp dictionary for activities that are spun up during the update process
            // If the update succeeds we'll copy these to the main _subscriptions dictionary.
            _dynamicActivities = new Dictionary<string, List<ActivityTrackPointCacheItem>>();
            _dynamicActivitiesIgnore = new List<string>();

            _dynamicUser = new Dictionary<string, List<UserTrackPoint>>();
            _dynamicUserIgnore = new List<string>();

            _pendingChanges = changeActions;
            _pendingWorkflowChange = true;
        }

        public void WorkflowChangeCommit()
        {
            Debug.Assert(_pendingWorkflowChange, "Workflow change is not pending - no change to commit");

            if (!_pendingWorkflowChange)
                return;

            if (!_isPrivate)
                throw new InvalidOperationException(ExecutionStringManager.ProfileIsNotPrivate);
            //
            // Remove items that have been deleted by this update
            // Must do all removes first as there may be a new action 
            // with the same qid as a previous action that is being removed
            if (null != _pendingChanges)
            {
                foreach (WorkflowChangeAction action in _pendingChanges)
                {
                    if (action is RemovedActivityAction)
                    {
                        //
                        // Remove all references to this activity that might exist in our caches
                        string qId = ((RemovedActivityAction)action).OriginalRemovedActivity.QualifiedName;
                        _activities.Remove(qId);
                        _activitiesIgnore.Remove(qId);
                        _user.Remove(qId);
                        _userIgnore.Remove(qId);
                    }
                }
            }
            //
            // Copy any pending cache items to the regular activity track point cache
            if ((null != _dynamicActivities) && (_dynamicActivities.Count > 0))
                foreach (KeyValuePair<string, List<ActivityTrackPointCacheItem>> kvp in _dynamicActivities)
                    _activities.Add(kvp.Key, kvp.Value);

            if ((null != _dynamicActivitiesIgnore) && (_dynamicActivitiesIgnore.Count > 0))
                _activitiesIgnore.AddRange(_dynamicActivitiesIgnore);

            if ((null != _dynamicUser) && (_dynamicUser.Count > 0))
                foreach (KeyValuePair<string, List<UserTrackPoint>> kvp in _dynamicUser)
                    _user.Add(kvp.Key, kvp.Value);

            if ((null != _dynamicUserIgnore) && (_dynamicUserIgnore.Count > 0))
                _userIgnore.AddRange(_dynamicUserIgnore);
            //
            // All done, clean up
            _dynamicActivities = null;
            _dynamicActivitiesIgnore = null;
            _dynamicUser = null;
            _dynamicUserIgnore = null;
            _pendingChanges = null;
            _pendingWorkflowChange = false;
        }

        public void WorkflowChangeRollback()
        {
            //
            // Just clean up, there isn't any work to rollback because
            // any subscriptions that may have been added for a pending add activity
            // won't ever be hit as the activities haven't been added to the tree.
            _dynamicActivities = null;
            _dynamicActivitiesIgnore = null;
            _dynamicUser = null;
            _dynamicUserIgnore = null;
            _pendingChanges = null;
            _pendingWorkflowChange = false;
        }

        #endregion

        #region Private Cache Methods

        /// <summary>
        /// Create the static qualifiedid to trackpoint map
        /// </summary>
        /// <param name="root"></param>
        private void CheckAllActivities(Activity activity)
        {
            CheckActivity((Activity)activity);
            //
            // Walk down the activity tree
            // Use EnabledActivities to get invisible activities
            // EnabledActivities will not return commented activities
            if (activity is CompositeActivity)
                foreach (Activity a in GetAllEnabledActivities((CompositeActivity)activity))
                    CheckAllActivities(a);
        }

        /// <summary>
        /// Recursively walk the activity tree and find all track points that match each activity
        /// </summary>
        /// <param name="activity"></param>
        private void CheckActivity(Activity activity)
        {
            //
            // Build caches of activity status change events
            string qId = activity.QualifiedName;
            List<ActivityTrackPointCacheItem> activities = null;
            if (CreateCacheItems(activity, out activities))
                CacheInsert(qId, activities);
            else
                _activitiesIgnore.Add(qId);

            //
            // Build caches of user events
            List<UserTrackPoint> user = null;
            if (CreateCacheItems(activity, out user))
                CacheInsert(qId, user);
            else
                _userIgnore.Add(qId);
        }

        /// <summary>
        /// Find all trackpoints that match an activity.
        /// </summary>
        /// <param name="activity">Activity for which to determine subscription needs</param>
        /// <param name="includes">List to be populated with matching track points</param>
        /// <returns>true if a subscription is needed; false if not</returns>
        private bool CreateCacheItems(Activity activity, out List<ActivityTrackPointCacheItem> includes)
        {
            includes = new List<ActivityTrackPointCacheItem>();
            //
            // Check if we have any trackpoints that match this activity
            foreach (ActivityTrackPoint point in _profile.ActivityTrackPoints)
            {
                List<ActivityExecutionStatus> events;
                bool hasCondition = false;
                if (point.IsMatch(activity, out events, out hasCondition))
                    includes.Add(new ActivityTrackPointCacheItem(point, events, hasCondition));
            }

            return (includes.Count > 0);
        }

        /// <summary>
        /// Find all trackpoints that match user events for an activity.
        /// </summary>
        /// <param name="activity">Activity for which to determine subscription needs</param>
        /// <param name="includes">List to be populated with matching track points</param>
        /// <returns>true if a subscription is needed; false if not</returns>
        private bool CreateCacheItems(Activity activity, out List<UserTrackPoint> includes)
        {
            includes = new List<UserTrackPoint>();
            //
            // Check if we have any trackpoints that match this activity
            foreach (UserTrackPoint point in _profile.UserTrackPoints)
            {
                if (point.IsMatch(activity))
                    includes.Add(point);
            }

            return (includes.Count > 0);
        }

        private void CacheInsert(string qualifiedID, List<ActivityTrackPointCacheItem> points)
        {
            //
            // Check to make sure the item isn't in the dictionary
            // If not add all track points
            Debug.Assert(!_activities.ContainsKey(qualifiedID), "QualifiedName is already in the activities cache");
            if (_activities.ContainsKey(qualifiedID))
                throw new InvalidOperationException(ExecutionStringManager.RTProfileActCacheDupKey);

            foreach (ActivityTrackPointCacheItem point in points)
                CacheInsert(qualifiedID, point);
        }

        private void CacheInsert(string qualifiedID, List<UserTrackPoint> points)
        {
            //
            // Check to make sure the item isn't in the dictionary
            // If not add all track points
            Debug.Assert(!_user.ContainsKey(qualifiedID), "QualifiedName is already in the user cache");
            if (_user.ContainsKey(qualifiedID))
                throw new InvalidOperationException(ExecutionStringManager.RTProfileActCacheDupKey);

            foreach (UserTrackPoint point in points)
                CacheInsert(qualifiedID, point);
        }

        private void CacheInsert(string qualifiedID, ActivityTrackPointCacheItem point)
        {
            List<ActivityTrackPointCacheItem> points = null;

            if (!_activities.TryGetValue(qualifiedID, out points))
            {
                points = new List<ActivityTrackPointCacheItem>();
                _activities.Add(qualifiedID, points);
            }
            points.Add(point);
        }

        private void CacheInsert(string qualifiedID, UserTrackPoint point)
        {
            List<UserTrackPoint> points = null;

            if (!_user.TryGetValue(qualifiedID, out points))
            {
                points = new List<UserTrackPoint>();
                _user.Add(qualifiedID, points);
            }
            points.Add(point);
        }

        private void CacheInsertUpdatePending(string qualifiedID, List<ActivityTrackPointCacheItem> points)
        {
            //
            // The activity has been added during a pending dynamic change
            // add it to a temporary lookup which will be copied to real cache 
            // when the dynamic update commits.
            if ((!_isPrivate) || (!_pendingWorkflowChange))
                throw new InvalidOperationException(ExecutionStringManager.ProfileIsNotPrivate);

            if (null == _dynamicActivities)
                throw new InvalidOperationException(ExecutionStringManager.RTProfileDynamicActCacheIsNull);

            List<ActivityTrackPointCacheItem> tmp = null;
            if (!_dynamicActivities.TryGetValue(qualifiedID, out tmp))
            {
                tmp = new List<ActivityTrackPointCacheItem>();
                _dynamicActivities.Add(qualifiedID, tmp);
            }

            foreach (ActivityTrackPointCacheItem point in points)
                tmp.Add(point);
        }

        private bool TryGetCacheItems(Activity activity, out List<ActivityTrackPointCacheItem> points)
        {
            points = null;
            if ((!_pendingWorkflowChange) || ((_pendingWorkflowChange) && (!IsPendingUpdateActivity(activity, true))))
            {
                //
                // A dynamic update is not in progress or this activity 
                // is not being added by the current dynamic update.
                // The main cache holds all matching track points
                return _activities.TryGetValue(activity.QualifiedName, out points);
            }
            else
            {
                //
                // Dynamic update is in progress
                return _dynamicActivities.TryGetValue(activity.QualifiedName, out points);
            }
        }

        private void CacheInsertUpdatePending(string qualifiedID, List<UserTrackPoint> points)
        {
            //
            // The activity has been added during a pending dynamic change
            // add it to a temporary lookup which will be copied to real cache 
            // when the dynamic update commits.
            if ((!_isPrivate) || (!_pendingWorkflowChange))
                throw new InvalidOperationException(ExecutionStringManager.ProfileIsNotPrivate);

            if (null == _dynamicUser)
                throw new InvalidOperationException(ExecutionStringManager.RTProfileDynamicActCacheIsNull);

            List<UserTrackPoint> tmp = null;
            if (!_dynamicUser.TryGetValue(qualifiedID, out tmp))
            {
                tmp = new List<UserTrackPoint>();
                _dynamicUser.Add(qualifiedID, tmp);
            }

            foreach (UserTrackPoint point in points)
                tmp.Add(point);
        }

        private bool TryGetCacheItems(Activity activity, out List<UserTrackPoint> points)
        {
            points = null;
            if ((!_pendingWorkflowChange) || ((_pendingWorkflowChange) && (!IsPendingUpdateActivity(activity, true))))
            {
                //
                // A dynamic update is not in progress or this activity 
                // is not being added by the current dynamic update.
                // The main cache holds all matching track points
                return _user.TryGetValue(activity.QualifiedName, out points);
            }
            else
            {
                //
                // Dynamic update is in progress
                return _dynamicUser.TryGetValue(activity.QualifiedName, out points);
            }
        }
        #endregion

        #region Private Methods


        // This function returns all the executable activities including secondary flow activities.
        public IList<Activity> GetAllEnabledActivities(CompositeActivity compositeActivity)
        {
            if (compositeActivity == null)
                throw new ArgumentNullException("compositeActivity");

            List<Activity> allActivities = new List<Activity>(compositeActivity.EnabledActivities);

            foreach (Activity secondaryFlowActivity in ((ISupportAlternateFlow)compositeActivity).AlternateFlowActivities)
            {
                if (!allActivities.Contains(secondaryFlowActivity))
                    allActivities.Add(secondaryFlowActivity);
            }

            return allActivities;
        }

        private bool IsPendingUpdateActivity(Activity activity, bool addedOnly)
        {
            //
            // If we don't have an update going on this method isn't valid
            if ((!_isPrivate) || (!_pendingWorkflowChange))
                throw new InvalidOperationException(ExecutionStringManager.ProfileIsNotPrivate);
            //
            // if we don't have any changes we're done
            if ((null == _pendingChanges || _pendingChanges.Count <= 0))
                return false;

            foreach (WorkflowChangeAction action in _pendingChanges)
            {
                string qualifiedId = null;
                if (action is ActivityChangeAction)
                {
                    if (action is AddedActivityAction)
                    {
                        qualifiedId = ((AddedActivityAction)action).AddedActivity.QualifiedName;
                    }
                    else if (action is RemovedActivityAction)
                    {
                        if (!addedOnly)
                            qualifiedId = ((RemovedActivityAction)action).OriginalRemovedActivity.QualifiedName;
                    }
                    else
                    {
                        Debug.Assert(false, ExecutionStringManager.UnknownActivityActionType);
                    }

                    if ((null != qualifiedId)
                        && (0 == String.Compare(activity.QualifiedName, qualifiedId, StringComparison.Ordinal)))
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        #endregion

        #region ICloneable Members

        object ICloneable.Clone()
        {
            return this.Clone();
        }

        internal RTTrackingProfile Clone()
        {
            return new RTTrackingProfile(this);
        }

        #endregion

        #region Contained Types

        private struct ActivityTrackPointCacheItem
        {
            internal ActivityTrackPointCacheItem(ActivityTrackPoint point, List<ActivityExecutionStatus> events, bool hasConditions)
            {
                if (null == point)
                    throw new ArgumentNullException("point");
                if (null == events)
                    throw new ArgumentNullException("events");

                Point = point;
                Events = events;
                HasLocationConditions = hasConditions;
            }

            internal ActivityTrackPoint Point;
            internal List<ActivityExecutionStatus> Events;
            internal bool HasLocationConditions;
        }

        #endregion
    }
}