Merge pull request #3213 from henricm/fix-for-win-securestring-to-bstr
[mono.git] / mcs / class / referencesource / System.Web / Cache / CacheDependency.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="CacheDependency.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>                                                                
5 //------------------------------------------------------------------------------
6
7 /*
8  * CacheDependency.cs
9  * 
10  * Copyright (c) 1998-1999, Microsoft Corporation
11  * 
12  */
13
14
15 namespace System.Web.Caching {
16     using System.Collections;
17     using System.Text;
18     using System.IO;
19     using System.Threading;
20     using System.Web.Util;
21     using System.Security.Permissions;
22     using System.Globalization;
23 #if USE_MEMORY_CACHE
24     using System.Collections.Generic;
25     using System.Collections.ObjectModel;
26     using System.Runtime.Caching;
27 #endif
28
29     internal interface ICacheDependencyChanged {
30         void DependencyChanged(Object sender, EventArgs e);
31     }
32
33
34     /// <devdoc>
35     /// <para>The <see langword='CacheDependency'/> class tracks cache dependencies, which can be files, 
36     ///    directories, or keys to other objects in the System.Web.Cache.Cache. When an object of this class
37     ///    is constructed, it immediately begins monitoring objects on which it is
38     ///    dependent for changes. This avoids losing the changes made between the time the
39     ///    object to cache is created and the time it is inserted into the
40     /// <see langword='Cache'/>.</para>
41     /// </devdoc>
42
43     // Overhead is 24 bytes + object header
44     public class CacheDependency : IDisposable {
45
46 #if DBG
47         bool                       _isUniqueIDInitialized;
48 #endif
49
50         string                     _uniqueID;              // used by HttpCachePolicy for the ETag
51         object                     _depFileInfos;          // files to monitor for changes, either a DepFileInfo or array of DepFileInfos 
52         object                     _entries;               // cache entries we are dependent on, either a string or array of strings 
53         ICacheDependencyChanged    _objNotify;             // Associated object to notify when a change occurs 
54         SafeBitVector32            _bits;                  // status bits for ready, used, changed, disposed  
55         DateTime                   _utcLastModified;       // Time of last modified item
56 #if USE_MEMORY_CACHE
57         HostFileChangeMonitor _fileChangeMonitor;
58         CacheEntryChangeMonitor _entryChangeMonitor;
59 #endif
60
61         static readonly string[]        s_stringsEmpty;
62         static readonly CacheEntry[]    s_entriesEmpty;
63         static readonly CacheDependency s_dependencyEmpty;
64         static readonly DepFileInfo[]   s_depFileInfosEmpty;
65
66         static readonly TimeSpan        FUTURE_FILETIME_BUFFER = new TimeSpan(0, 1, 0); // See VSWhidbey 400917
67
68         const int BASE_INIT             = 0x01;
69         const int USED                  = 0x02;
70         const int CHANGED               = 0x04;
71         const int BASE_DISPOSED         = 0x08;
72         const int WANTS_DISPOSE         = 0x10;
73         const int DERIVED_INIT          = 0x20;
74         const int DERIVED_DISPOSED      = 0x40;
75
76         internal class DepFileInfo {
77             internal string             _filename;
78             internal FileAttributesData _fad;
79         }
80
81         static CacheDependency() {
82             s_stringsEmpty = new string[0];
83             s_entriesEmpty = new CacheEntry[0];
84             s_dependencyEmpty = new CacheDependency(0);
85             s_depFileInfosEmpty = new DepFileInfo[0];
86         }
87
88         // creates an empty dependency which is used only by s_dependencyEmpty
89         private CacheDependency(int bogus) {
90             Debug.Assert(s_dependencyEmpty == null, "s_dependencyEmpty == null");
91         }
92
93
94
95         protected CacheDependency() {
96             Init(true, null, null, null, DateTime.MaxValue);
97         }
98
99
100         /// <devdoc>
101         /// <para>Initializes a new instance of the System.Web.Cache.CacheDependency class. The new instance 
102         ///    monitors a file or directory for changes.</para>
103         /// </devdoc>
104         public CacheDependency(string filename) :
105             this (filename, DateTime.MaxValue) {
106         }
107             
108
109         public CacheDependency(string filename, DateTime start) {
110             if (filename == null) {
111                 return;
112             }
113
114             DateTime utcStart = DateTimeUtil.ConvertToUniversalTime(start);
115             string[] filenames = new string[1] {filename};
116             Init(true, filenames, null, null, utcStart);
117
118         }
119
120
121         /// <devdoc>
122         /// <para>Initializes a new instance of the System.Web.Cache.CacheDependency class. The new instance monitors an array 
123         ///    files or directories for changes.</para>
124         /// </devdoc>
125         public CacheDependency(string[] filenames) {
126             Init(true, filenames, null, null, DateTime.MaxValue);
127         }
128
129
130         public CacheDependency(string[] filenames, DateTime start) {
131             DateTime utcStart = DateTimeUtil.ConvertToUniversalTime(start);
132             Init(true, filenames, null, null, utcStart);
133         }
134
135
136         /// <devdoc>
137         /// <para>Initializes a new instance of the System.Web.Cache.CacheDependency class. The new instance monitors an 
138         ///    array files, directories, and cache keys for changes.</para>
139         /// </devdoc>
140         public CacheDependency(string[] filenames, string[] cachekeys) {
141             Init(true, filenames, cachekeys, null, DateTime.MaxValue);
142         }
143
144
145         public CacheDependency(string[] filenames, string[] cachekeys, DateTime start) {
146             DateTime utcStart = DateTimeUtil.ConvertToUniversalTime(start);
147             Init(true, filenames, cachekeys, null, utcStart);
148         }
149
150
151         public CacheDependency(string[] filenames, string[] cachekeys, CacheDependency dependency) {
152             Init(true, filenames, cachekeys, dependency, DateTime.MaxValue);
153         }
154
155
156         public CacheDependency(string[] filenames, string[] cachekeys, CacheDependency dependency, DateTime start) {
157             DateTime utcStart = DateTimeUtil.ConvertToUniversalTime(start);
158             Init(true, filenames, cachekeys, dependency, utcStart);
159         }
160
161         internal CacheDependency(int dummy, string filename) :
162             this(dummy, filename, DateTime.MaxValue) {
163         }
164             
165         internal CacheDependency(int dummy, string filename, DateTime utcStart) {
166             if (filename == null) {
167                 return;
168             }
169
170             string[] filenames = new string[1] {filename};
171             Init(false, filenames, null, null, utcStart);
172
173         }
174
175         internal CacheDependency(int dummy, string[] filenames) {
176             Init(false, filenames, null, null, DateTime.MaxValue);
177         }
178
179         internal CacheDependency(int dummy, string[] filenames, DateTime utcStart) {
180             Init(false, filenames, null, null, utcStart);
181         }
182
183         internal CacheDependency(int dummy, string[] filenames, string[] cachekeys) {
184             Init(false, filenames, cachekeys, null, DateTime.MaxValue);
185         }
186
187         internal CacheDependency(int dummy, string[] filenames, string[] cachekeys, DateTime utcStart) {
188             Init(false, filenames, cachekeys, null, utcStart);
189         }
190
191         internal CacheDependency(int dummy, string[] filenames, string[] cachekeys, CacheDependency dependency) {
192             Init(false, filenames, cachekeys, dependency, DateTime.MaxValue);
193         }
194
195         internal CacheDependency(int dummy, string[] filenames, string[] cachekeys, CacheDependency dependency, DateTime utcStart) {
196             Init(false, filenames, cachekeys, dependency, utcStart);
197         }
198
199 #if USE_MEMORY_CACHE
200         void OnChangedCallback(object state) {
201             Debug.Trace("CacheDependencyFileChange", "OnChangedCallback fired");
202             NotifyDependencyChanged(this, EventArgs.Empty);            
203         }
204
205         void InitForMemoryCache(bool isPublic, string[] filenamesArg, string[] cachekeysArg, CacheDependency dependency, DateTime utcStart) {
206             bool dispose = true;
207             try {
208                 MemCache memCache = HttpRuntime.CacheInternal as MemCache;
209                 _bits = new SafeBitVector32(0);
210                 _utcLastModified = DateTime.MinValue;
211                 IList<String> files = filenamesArg;
212                 IList<String> keys = cachekeysArg;
213                 if (dependency != null) {
214                     ReadOnlyCollection<string> filePaths = (dependency._fileChangeMonitor != null) ? dependency._fileChangeMonitor.FilePaths : null;
215                     ReadOnlyCollection<string> cacheKeys = (dependency._entryChangeMonitor != null) ? dependency._entryChangeMonitor.CacheKeys : null;
216                     if (filePaths != null || filenamesArg != null) {
217                         if (filePaths == null) {
218                             files = filenamesArg;
219                         }
220                         else if (filenamesArg == null) {
221                             files = filePaths;
222                         }
223                         else {
224                             files = new List<String>(filenamesArg.Length + filePaths.Count);
225                             foreach (string f in filenamesArg) {
226                                 files.Add(f);
227                             }
228                             foreach (string f in filePaths) {
229                                 files.Add(f);
230                             }
231                         }
232                     }
233                     if (cacheKeys != null || cachekeysArg != null) {
234                         if (cacheKeys == null) {
235                             keys = cachekeysArg;
236                         }
237                         else if (cachekeysArg == null) {
238                             keys = cacheKeys;
239                         }
240                         else {
241                             keys = new List<String>(cachekeysArg.Length + cacheKeys.Count);
242                             foreach (string f in cachekeysArg) {
243                                 keys.Add(f);
244                             }
245                             foreach (string f in cacheKeys) {
246                                 keys.Add(f);
247                             }
248                         }
249                     }
250                 }
251                 
252                 _fileChangeMonitor = (files != null) ? new HostFileChangeMonitor(files) : null;
253                 _entryChangeMonitor = (keys != null) ? memCache.CreateCacheEntryChangeMonitor(keys, isPublic) : null;
254                 
255                 string uniqueId = null;
256                 
257                 if (_fileChangeMonitor != null) {
258                     _utcLastModified = _fileChangeMonitor.LastModified.UtcDateTime;
259                     uniqueId = _fileChangeMonitor.UniqueId;
260                     _fileChangeMonitor.NotifyOnChanged(new OnChangedCallback(OnChangedCallback));
261                 }
262                 if (_entryChangeMonitor != null) {
263                     DateTime utcLastModified = _entryChangeMonitor.LastModified.UtcDateTime;
264                     if (utcLastModified > _utcLastModified) {
265                         _utcLastModified = utcLastModified;
266                     }
267                     uniqueId += _entryChangeMonitor.UniqueId;
268                     _entryChangeMonitor.NotifyOnChanged(new OnChangedCallback(OnChangedCallback));
269                 }
270                 
271                 _uniqueID = uniqueId;
272 #if DBG
273                 _isUniqueIDInitialized = true;
274 #endif
275                 // check if file has changed since the start time
276                 if (utcStart < DateTime.MaxValue) {
277                     if (_utcLastModified > utcStart
278                         && !(_utcLastModified - DateTime.UtcNow > FUTURE_FILETIME_BUFFER)) {   // See VSWhidbey 400917
279                         _bits[CHANGED] = true;
280                     }
281                 }
282
283                 _bits[BASE_INIT] = true;
284                 if (dependency != null && dependency._bits[CHANGED]) {
285                     _bits[CHANGED] = true;
286                 }
287                 if (_bits[WANTS_DISPOSE] || _bits[CHANGED]) {
288                     Debug.Trace("CacheDependencyInit", "WANTS_DISPOSE or CHANGED.  InitForMemoryCache calling DisposeInternal");
289                     DisposeInternal();
290                 }
291                 dispose = false;
292             }
293             finally {
294                 if (dispose) {
295                     _bits[BASE_INIT] = true;
296                     Debug.Trace("CacheDependencyInit", "\n\nERROR in CacheDependency.InitForMemoryCache, calling DisposeInternal");
297                     DisposeInternal();
298                 }
299             }
300         }
301 #endif
302
303         void Init(bool isPublic, string[] filenamesArg, string[] cachekeysArg, CacheDependency dependency, DateTime utcStart) {
304 #if USE_MEMORY_CACHE
305             if (CacheInternal.UseMemoryCache) {
306                 InitForMemoryCache(isPublic, filenamesArg, cachekeysArg, dependency, utcStart);
307                 return;
308             }
309 #endif
310             DepFileInfo[]   depFileInfos = s_depFileInfosEmpty;
311             CacheEntry[]    depEntries = s_entriesEmpty;
312             string []       filenames, cachekeys;
313             CacheInternal   cacheInternal;
314
315             _bits = new SafeBitVector32(0);
316
317             // copy array argument contents so they can't be changed beneath us
318             if (filenamesArg != null) {
319                 filenames = (string []) filenamesArg.Clone();
320             }
321             else {
322                 filenames = null;
323             }
324
325             if (cachekeysArg != null) {
326                 cachekeys = (string []) cachekeysArg.Clone();
327             }
328             else {
329                 cachekeys = null;
330             }
331
332             _utcLastModified = DateTime.MinValue;
333
334             try {
335                 // validate filenames array
336                 if (filenames == null) {
337                     filenames = s_stringsEmpty;
338                 }
339                 else {
340                     foreach (string f in filenames) {
341                         if (f == null) {
342                             throw new ArgumentNullException("filenamesArg");
343                         }
344
345                         // demand PathDiscovery if public
346                         if (isPublic) {
347                             InternalSecurityPermissions.PathDiscovery(f).Demand();
348                         }
349                     }
350                 }
351
352                 if (cachekeys == null) {
353                     cachekeys = s_stringsEmpty;
354                 }
355                 else {
356                     // validate cachekeys array
357                     foreach (string k in cachekeys) {
358                         if (k == null) {
359                             throw new ArgumentNullException("cachekeysArg");
360                         }
361                     }
362                 }
363
364                 // copy all parts of another dependency if provided
365                 if (dependency == null) {
366                     dependency = s_dependencyEmpty;
367                 }
368                 else {
369                     if (dependency.GetType() != s_dependencyEmpty.GetType()) {
370                         throw new ArgumentException(SR.GetString(SR.Invalid_Dependency_Type));
371                     }
372
373                     // Copy the parts of the dependency we need before
374                     // we reference them, as the dependency can change
375                     // underneath us.
376                     object d_depFileInfos = dependency._depFileInfos;
377                     object d_entries = dependency._entries;
378                     DateTime d_lastModified = dependency._utcLastModified;
379
380                     // if the dependency we're copying has changed, we're done
381                     if (dependency._bits[CHANGED]) {
382                         _bits[CHANGED] = true;
383                         // There is nothing to dispose because we haven't started
384                         // monitoring anything yet.  But we call DisposeInternal in
385                         // order to set the WANTS_DISPOSE bit.
386                         DisposeInternal();
387                         return;
388                     }
389
390                     // copy depFileInfos
391                     if (d_depFileInfos != null) {
392                         if (d_depFileInfos is DepFileInfo) {
393                             depFileInfos = new DepFileInfo[1] {(DepFileInfo) d_depFileInfos};
394                         }
395                         else {
396                             depFileInfos = (DepFileInfo[]) (d_depFileInfos);
397
398                         }
399
400                         // verify that the object was fully constructed
401                         // and that we have permission to discover the file
402                         foreach (DepFileInfo depFileInfo in depFileInfos) {
403                             string f = depFileInfo._filename;
404                             
405                             if (f == null) {
406                                 _bits[CHANGED] = true;
407                                 // There is nothing to dispose because we haven't started
408                                 // monitoring anything yet.  But we call DisposeInternal in
409                                 // order to set the WANTS_DISPOSE bit.
410                                 DisposeInternal();
411                                 return;
412                             }
413
414                             // demand PathDiscovery if public
415                             if (isPublic) {
416                                 InternalSecurityPermissions.PathDiscovery(f).Demand();
417                             }
418                         }
419                     }
420
421                     // copy cache entries
422                     if (d_entries != null) {
423                         if (d_entries is CacheEntry) {
424                             depEntries = new CacheEntry[1] {(CacheEntry) (d_entries)};
425                         }
426                         else {
427                             depEntries = (CacheEntry[]) (d_entries);
428                             // verify that the object was fully constructed
429                             foreach (CacheEntry entry in depEntries) {
430                                 if (entry == null) {
431                                     _bits[CHANGED] = true;
432                                     // There is nothing to dispose because we haven't started
433                                     // monitoring anything yet.  But we call DisposeInternal in
434                                     // order to set the WANTS_DISPOSE bit.
435                                     DisposeInternal();
436                                     return;
437                                 }
438                             }
439                         }
440                     }
441
442                     _utcLastModified = d_lastModified;
443                 }
444
445                 // Monitor files for changes
446                 int lenMyDepFileInfos = depFileInfos.Length + filenames.Length;
447                 if (lenMyDepFileInfos > 0) {
448                     DepFileInfo[] myDepFileInfos = new DepFileInfo[lenMyDepFileInfos];
449                     FileChangeEventHandler handler = new FileChangeEventHandler(this.FileChange);
450                     FileChangesMonitor fmon = HttpRuntime.FileChangesMonitor;
451
452                     int i;
453                     for (i = 0; i < lenMyDepFileInfos; i++) {
454                         myDepFileInfos[i] = new DepFileInfo();
455                     }
456
457                     // monitor files from the existing dependency
458                     // note that we don't check for start times in the existing dependency
459                     i = 0;
460                     foreach (DepFileInfo depFileInfo in depFileInfos) {
461                         string  f = depFileInfo._filename;
462                         fmon.StartMonitoringPath(f, handler, out myDepFileInfos[i]._fad);
463                         myDepFileInfos[i]._filename = f;
464                         i++;
465                     }
466
467                     // monitor new files
468                     DateTime    utcNow = DateTime.MinValue;
469                     foreach (string f in filenames) {
470                         DateTime utcLastWrite = fmon.StartMonitoringPath(f, handler, out myDepFileInfos[i]._fad);
471                         myDepFileInfos[i]._filename = f;
472                         i++;
473
474                         if (utcLastWrite > _utcLastModified) {
475                             _utcLastModified = utcLastWrite;
476                         }
477
478                         // check if file has changed since the start time
479                         if (utcStart < DateTime.MaxValue) {
480                             if (utcNow == DateTime.MinValue) {
481                                 utcNow = DateTime.UtcNow;
482                             }
483                             
484                             Debug.Trace("CacheDependencyInit", "file=" + f + "; utcStart=" + utcStart + "; utcLastWrite=" + utcLastWrite);
485                             if (utcLastWrite >= utcStart &&
486                                 !(utcLastWrite - utcNow > FUTURE_FILETIME_BUFFER)) {   // See VSWhidbey 400917
487                                 Debug.Trace("CacheDependencyInit", "changes occurred since start time for file " + f);
488                                 _bits[CHANGED] = true;
489                                 break;
490                             }
491                         }
492                     }
493
494                     if (myDepFileInfos.Length == 1) {
495                         _depFileInfos = myDepFileInfos[0];
496                     }
497                     else {
498                         _depFileInfos = myDepFileInfos;
499                     }
500                 }
501
502                 // Monitor other cache entries for changes
503                 int lenMyEntries = depEntries.Length + cachekeys.Length;
504                 if (lenMyEntries > 0 && !_bits[CHANGED]) {
505                     CacheEntry[] myEntries = new CacheEntry[lenMyEntries];
506
507                     // Monitor entries from the existing cache dependency
508                     int i = 0;
509                     foreach (CacheEntry entry in depEntries) {
510                         entry.AddCacheDependencyNotify(this);
511                         myEntries[i++] = entry;
512                     }
513
514                     // Monitor new entries specified for this depenedency
515                     // Entries must be added to cache, and created before the startTime
516                     cacheInternal = HttpRuntime.CacheInternal;
517                     foreach (string k in cachekeys) {
518                         CacheEntry entry = (CacheEntry) cacheInternal.DoGet(isPublic, k, CacheGetOptions.ReturnCacheEntry);
519                         if (entry != null) {
520                             entry.AddCacheDependencyNotify(this);
521                             myEntries[i++] = entry;
522
523                             if (entry.UtcCreated > _utcLastModified) {
524                                 _utcLastModified = entry.UtcCreated;
525                             }
526
527                             if (    entry.State != CacheEntry.EntryState.AddedToCache || 
528                                     entry.UtcCreated > utcStart) {
529
530 #if DBG
531                                 if (entry.State != CacheEntry.EntryState.AddedToCache) {
532                                     Debug.Trace("CacheDependencyInit", "Entry is not in cache, considered changed:" + k);
533                                 }
534                                 else {
535                                     Debug.Trace("CacheDependencyInit", "Changes occurred to entry since start time:" + k);
536                                 }
537 #endif
538
539                                 _bits[CHANGED] = true;
540                                 break;
541                             }
542                         }
543                         else {
544                             Debug.Trace("CacheDependencyInit", "Cache item not found to create dependency on:" + k);
545                             _bits[CHANGED] = true;
546                             break;
547                         }
548                     }
549
550                     if (myEntries.Length == 1) {
551                         _entries = myEntries[0];
552                     }
553                     else {
554                         _entries = myEntries;
555                     }
556                 }
557
558                 _bits[BASE_INIT] = true;
559                 if (dependency._bits[CHANGED]) {
560                     _bits[CHANGED] = true;
561                 }
562  
563                 if (_bits[WANTS_DISPOSE] || _bits[CHANGED]) {
564                     DisposeInternal();
565                 }
566
567                 Debug.Assert(_objNotify == null, "_objNotify == null");
568             }
569             catch {
570                 // derived constructor will not execute due to the throw,
571                 // so we just force a dispose on ourselves
572                 _bits[BASE_INIT] = true;
573                 DisposeInternal();
574                 throw;
575             }
576             finally {
577                 InitUniqueID();
578             }
579         }
580
581
582         public void Dispose() {
583             // Set this bit just in case our derived ctor forgot to call FinishInit()
584             _bits[DERIVED_INIT] = true;
585                 
586             if (Use()) {
587                 // Do the dispose only if the cache has not already used us
588                 DisposeInternal();
589             }
590         }
591
592         protected internal void FinishInit() {
593             _bits[DERIVED_INIT] = true;
594
595             if (_bits[WANTS_DISPOSE]) {
596                 DisposeInternal();
597             }
598         }
599
600         /*
601          * Shutdown all dependency monitoring and firing of NotifyDependencyChanged notification.
602          */
603         internal void DisposeInternal() {
604             _bits[WANTS_DISPOSE] = true;
605
606             if (_bits[DERIVED_INIT]) {
607                 if (_bits.ChangeValue(DERIVED_DISPOSED, true)) {
608                     // Dispose derived classes
609                     DependencyDispose();
610                 }
611             }
612
613             if (_bits[BASE_INIT]) {
614                 if (_bits.ChangeValue(BASE_DISPOSED, true)) {
615                     // Dispose ourself
616                     DisposeOurself();
617                 }
618             }
619         }
620
621         // Allow derived class to dispose itself
622
623         protected virtual void DependencyDispose() {
624             // We do our own dispose work in DisposeOurself, so that
625             // we don't rely on derived classes calling their base
626             // DependencyDispose for us to function correctly.
627         }
628
629         void DisposeOurself() {
630             // guarantee that we execute only once if an exception
631             // is thrown from this function by nulling fields before
632             // we access them
633             object l_depFileInfos = _depFileInfos;
634             object l_entries = _entries;
635
636             _objNotify = null;
637             _depFileInfos = null;
638             _entries = null;
639
640             // stop monitoring files
641             if (l_depFileInfos != null) {
642                 FileChangesMonitor fmon = HttpRuntime.FileChangesMonitor;
643
644                 DepFileInfo oneDepFileInfo = l_depFileInfos as DepFileInfo;
645                 if (oneDepFileInfo != null) {
646                     fmon.StopMonitoringPath(oneDepFileInfo._filename, this);
647                 }
648                 else {
649                     DepFileInfo[] depFileInfos = (DepFileInfo[]) l_depFileInfos;
650                     foreach (DepFileInfo depFileInfo in depFileInfos) {
651                         // ensure that we handle partially contructed
652                         // objects by checking filename for null
653                         string  filename = depFileInfo._filename;
654                         if (filename != null) {
655                             fmon.StopMonitoringPath(filename, this);
656                         }
657                     }
658                 }
659             }
660
661             // stop monitoring cache items
662             if (l_entries != null) {
663                 CacheEntry oneEntry = l_entries as CacheEntry;
664                 if (oneEntry != null) {
665                     oneEntry.RemoveCacheDependencyNotify(this);
666                 }
667                 else {
668                     CacheEntry[] entries = (CacheEntry[]) l_entries;
669                     foreach (CacheEntry entry in entries) {
670                         // ensure that we handle partially contructed
671                         // objects by checking entry for null
672                         if (entry != null) {
673                             entry.RemoveCacheDependencyNotify(this);
674                         }
675                     }
676                 }
677             }
678
679 #if USE_MEMORY_CACHE
680             if (_fileChangeMonitor != null) {
681                 _fileChangeMonitor.Dispose();
682             }
683             if (_entryChangeMonitor != null) {
684                 _entryChangeMonitor.Dispose();
685             }
686 #endif
687         }
688
689         // allow the first user to declare ownership
690         internal bool Use() {
691             return _bits.ChangeValue(USED, true);
692         }
693
694         //
695         // Has a dependency changed?
696         //
697
698         public bool HasChanged {
699             get {return _bits[CHANGED];}
700         }
701
702
703         public DateTime UtcLastModified {
704             get {
705                 return _utcLastModified;
706             }
707         }
708
709
710         protected void SetUtcLastModified(DateTime utcLastModified) {
711             _utcLastModified = utcLastModified;
712         }
713
714         //
715         // Add/remove an NotifyDependencyChanged notification.
716         //
717         internal void SetCacheDependencyChanged(ICacheDependencyChanged objNotify) {
718             Debug.Assert(_objNotify == null, "_objNotify == null");
719
720             // Set this bit just in case our derived ctor forgot to call FinishInit()
721             _bits[DERIVED_INIT] = true;
722                 
723             if (!_bits[BASE_DISPOSED]) {
724                 _objNotify = objNotify;
725             }
726         }
727
728         internal void AppendFileUniqueId(DepFileInfo depFileInfo, StringBuilder sb) {
729             FileAttributesData fad = depFileInfo._fad;
730                 
731             if (fad == null) {
732                 fad = FileAttributesData.NonExistantAttributesData;
733             }
734
735             sb.Append(depFileInfo._filename);
736             sb.Append(fad.UtcLastWriteTime.Ticks.ToString("d", NumberFormatInfo.InvariantInfo));
737             sb.Append(fad.FileSize.ToString(CultureInfo.InvariantCulture));
738         }
739
740         void InitUniqueID() {
741             StringBuilder   sb = null;
742             object          l_depFileInfos, l_entries;
743                 
744 #if !FEATURE_PAL // no File Change Monitoring
745             // get unique id from files
746             l_depFileInfos = _depFileInfos;
747             if (l_depFileInfos != null) {
748                 DepFileInfo oneDepFileInfo = l_depFileInfos as DepFileInfo;
749                 if (oneDepFileInfo != null) {
750                      sb = new StringBuilder();
751                     AppendFileUniqueId(oneDepFileInfo, sb);
752                 }
753                 else {
754                     DepFileInfo[] depFileInfos = (DepFileInfo[]) l_depFileInfos;
755                     foreach (DepFileInfo depFileInfo in depFileInfos) {
756                         // ensure that we handle partially contructed
757                         // objects by checking filename for null
758                         if (depFileInfo._filename != null) {
759                             if (sb == null)
760                                 sb = new StringBuilder();
761                             AppendFileUniqueId(depFileInfo, sb);
762                         }
763                     }
764                 }
765             }
766             
767 #endif // !FEATURE_PAL
768             // get unique id from cache entries
769             l_entries = _entries;
770             if (l_entries != null) {
771                 CacheEntry oneEntry = l_entries as CacheEntry;
772                 if (oneEntry != null) {
773                     if (sb == null)
774                         sb = new StringBuilder();
775                     sb.Append(oneEntry.Key);
776                     sb.Append(oneEntry.UtcCreated.Ticks.ToString(CultureInfo.InvariantCulture));
777                 }
778                 else {
779                     CacheEntry[] entries = (CacheEntry[]) l_entries;
780                     foreach (CacheEntry entry in entries) {
781                         // ensure that we handle partially contructed
782                         // objects by checking entry for null
783                         if (entry != null) {
784                             if (sb == null)
785                                 sb = new StringBuilder();
786                             sb.Append(entry.Key);
787                             sb.Append(entry.UtcCreated.Ticks.ToString(CultureInfo.InvariantCulture));
788                         }
789                     }
790                 }
791             }
792
793             if (sb != null)
794                 _uniqueID = sb.ToString();
795
796 #if DBG
797             _isUniqueIDInitialized = true;
798 #endif
799         }
800
801         public virtual string GetUniqueID() {
802 #if DBG
803             Debug.Assert(_isUniqueIDInitialized == true, "_isUniqueIDInitialized == true");
804 #endif
805             return _uniqueID;
806         }
807
808         //
809         // Return the cacheEntries monitored by this dependency
810         // 
811         internal CacheEntry[] CacheEntries {
812             get {
813                 if (_entries == null) {
814                     return null;
815                 }
816
817                 CacheEntry oneEntry = _entries as CacheEntry;
818                 if (oneEntry != null) {
819                     return new CacheEntry[1] {oneEntry};
820                 }
821
822                 return (CacheEntry[]) _entries;
823             }
824         }
825
826         //
827         // This object has changed, so fire the NotifyDependencyChanged event.
828         // We only allow this event to be fired once.
829         //
830
831         protected void NotifyDependencyChanged(Object sender, EventArgs e) {
832             if (_bits.ChangeValue(CHANGED, true)) {
833                 _utcLastModified = DateTime.UtcNow;
834
835                 ICacheDependencyChanged objNotify = _objNotify;
836                 if (objNotify != null && !_bits[BASE_DISPOSED]) {
837                     Debug.Trace("CacheDependencyNotifyDependencyChanged", "change occurred");
838                     objNotify.DependencyChanged(sender, e);
839                 }
840
841                 DisposeInternal();
842             }
843         }
844
845         //
846         // ItemRemoved is called when a cache entry we are monitoring has been removed.
847         //
848         internal void ItemRemoved() {
849             NotifyDependencyChanged(this, EventArgs.Empty);
850         }
851
852         //
853         // FileChange is called when a file we are monitoring has changed.
854         //
855         void FileChange(Object sender, FileChangeEvent e) {
856             Debug.Trace("CacheDependencyFileChange", "FileChange file=" + e.FileName + ";Action=" + e.Action);
857             NotifyDependencyChanged(sender, e);
858         }
859
860         //
861         //  This will examine the dependency and determine if it's ONLY a file dependency or not 
862         //
863         internal virtual bool IsFileDependency()
864         {
865 #if USE_MEMORY_CACHE
866             if (CacheInternal.UseMemoryCache) {
867                 if (_entryChangeMonitor != null) {
868                     return false;
869                 }
870                 
871                 if (_fileChangeMonitor != null) {
872                     return true;
873                 }
874                 return false;
875             }
876 #endif
877
878             object depInfos, l_entries;
879
880             // Check and see if we are dependent on any cache entries
881             l_entries = _entries;
882             if (l_entries != null) {
883                 CacheEntry oneEntry = l_entries as CacheEntry;
884                 if (oneEntry != null) {
885                     return false;
886                 }
887                 else {
888                     CacheEntry[] entries = (CacheEntry[]) l_entries;
889                     if (entries != null && entries.Length > 0) {
890                         return false;
891                     }
892                 }
893             }
894
895             depInfos = _depFileInfos;
896             if (depInfos != null) {
897                 DepFileInfo oneDepFileInfo = depInfos as DepFileInfo;
898                 if (oneDepFileInfo != null) {
899                     return true;
900                 }
901                 else {
902                     DepFileInfo[] depFileInfos = (DepFileInfo[]) depInfos;
903                     if (depFileInfos != null && depFileInfos.Length > 0) {
904                         return true;
905                     }
906                 }
907             }
908
909             return false;
910         }
911
912         /// <summary>
913         /// This method will return only the file dependencies from this dependency
914         /// </summary>
915         /// <returns></returns>
916         public virtual string[] GetFileDependencies()
917         {
918 #if USE_MEMORY_CACHE
919             if (CacheInternal.UseMemoryCache) {
920                 if (_fileChangeMonitor != null) {
921                     ReadOnlyCollection<string> paths = _fileChangeMonitor.FilePaths;
922                     if (paths != null && paths.Count > 0) {
923                         string[] aryPaths = new string[paths.Count];
924                         for (int i = 0; i < aryPaths.Length; i++) {
925                             aryPaths[i] = paths[i];
926                         }
927                         return aryPaths;
928                     }
929                 }
930                 return null;
931             }
932 #endif
933             object depInfos = _depFileInfos;
934
935             if (depInfos != null) {
936                 DepFileInfo oneDepFileInfo = depInfos as DepFileInfo;
937                 if (oneDepFileInfo != null) {
938                     return new string[] {oneDepFileInfo._filename};
939                 }
940                 else {
941                     DepFileInfo[] depFileInfos = (DepFileInfo[]) depInfos;
942                     string[] names = new string[depFileInfos.Length];
943                     for (int i = 0; i < depFileInfos.Length; i++) {
944                         names[i] = depFileInfos[i]._filename;
945                     }
946
947                     return names;
948                 }
949             }
950
951             return null;
952         }
953     }
954
955     public sealed class AggregateCacheDependency : CacheDependency, ICacheDependencyChanged {
956         ArrayList   _dependencies;
957         bool        _disposed;
958
959
960         public AggregateCacheDependency() {
961             // The ctor of every class derived from CacheDependency must call this.
962             FinishInit();
963         }
964
965
966         public void Add(params CacheDependency [] dependencies) {
967             DateTime utcLastModified = DateTime.MinValue;
968
969             if (dependencies == null) {
970                 throw new ArgumentNullException("dependencies");
971             }
972
973             // copy array argument contents so they can't be changed beneath us
974             dependencies = (CacheDependency []) dependencies.Clone();
975
976             // validate contents
977             foreach (CacheDependency d in dependencies) {
978                 if (d == null) {
979                     throw new ArgumentNullException("dependencies");
980                 }
981
982                 if (!d.Use()) {
983                     throw new InvalidOperationException(SR.GetString(SR.Cache_dependency_used_more_that_once));
984                 }
985             }
986
987             // add dependencies, and check if any have changed
988             bool hasChanged = false;
989             lock (this) {
990                 if (!_disposed) {
991                     if (_dependencies == null) {
992                         _dependencies = new ArrayList();
993                     }
994
995                     _dependencies.AddRange(dependencies);
996
997                     foreach (CacheDependency d in dependencies) {
998                         d.SetCacheDependencyChanged(this);
999
1000                         if (d.UtcLastModified > utcLastModified) {
1001                             utcLastModified = d.UtcLastModified;
1002                         }
1003
1004                         if (d.HasChanged) {
1005                             hasChanged = true;
1006                             break;
1007                         }
1008                     }
1009                 }
1010             }
1011
1012             SetUtcLastModified(utcLastModified);
1013
1014             // if a dependency has changed, notify others that we have changed.
1015             if (hasChanged) {
1016                 NotifyDependencyChanged(this, EventArgs.Empty);
1017             }
1018         }
1019
1020         // Dispose our dependencies. Note that the call to this
1021         // function is thread safe.
1022
1023         protected override void DependencyDispose() {
1024             CacheDependency[] dependencies = null;
1025
1026             lock (this) {
1027                 _disposed = true;
1028                 if (_dependencies != null) {
1029                     dependencies = (CacheDependency[]) _dependencies.ToArray(typeof(CacheDependency));
1030                     _dependencies = null;
1031                 }
1032             }
1033
1034             if (dependencies != null) {
1035                 foreach (CacheDependency d in dependencies) {
1036                     d.DisposeInternal();
1037                 }
1038             }
1039         }
1040
1041         // Forward call from the aggregate to the CacheEntry
1042
1043         /// <internalonly/>
1044         void ICacheDependencyChanged.DependencyChanged(Object sender, EventArgs e) {
1045             NotifyDependencyChanged(sender, e);
1046         }
1047
1048
1049         public override string GetUniqueID() {
1050             StringBuilder sb = null;
1051             CacheDependency[] dependencies = null;
1052
1053             //VSWhidbey 354570: return null if this AggregateCacheDependency cannot otherwise return a unique ID
1054             if (_dependencies == null) {
1055                 return null;
1056             }
1057
1058             lock (this) {
1059                 if (_dependencies != null) {
1060                     dependencies = (CacheDependency[]) _dependencies.ToArray(typeof(CacheDependency));
1061                 }
1062             }
1063             
1064             if (dependencies != null) {
1065                 foreach (CacheDependency dependency in dependencies) {
1066                     string id = dependency.GetUniqueID();
1067
1068                     if (id == null) {
1069                         // When AggregateCacheDependency contains a dependency for which GetUniqueID() returns null, 
1070                         // it should return null itself.  This is because it can no longer generate a UniqueID that 
1071                         // is guaranteed to be different when any of the dependencies change.
1072                         return null;
1073                     }
1074
1075                     if (sb == null) {
1076                         sb = new StringBuilder();
1077                     }
1078                     sb.Append(id);
1079                 }
1080             }
1081
1082             return sb != null ? sb.ToString() : null;
1083         }
1084
1085         internal CacheDependency[] GetDependencyArray()
1086         {
1087             CacheDependency[] dependencies = null;
1088
1089             lock (this) {
1090                 if (_dependencies != null) {
1091                     dependencies = (CacheDependency[]) _dependencies.ToArray(typeof(CacheDependency));
1092                 }
1093             }
1094
1095             return dependencies;
1096         }
1097
1098         //
1099         //  This will examine the dependencies and only return true if ALL dependencies are file dependencies
1100         //
1101         internal override bool IsFileDependency()
1102         {
1103             CacheDependency[] dependencies = null;
1104
1105             dependencies = GetDependencyArray();
1106             if (dependencies == null) {
1107                 return false;
1108             }
1109
1110             foreach (CacheDependency d in dependencies) {
1111                 // We should only check if the type is either CacheDependency or the Aggregate.
1112                 // Anything else, we can't guarantee that it's a file only dependency.
1113                 if ( ! object.ReferenceEquals(d.GetType(), typeof(CacheDependency)) &&
1114                      ! object.ReferenceEquals(d.GetType(), typeof(AggregateCacheDependency)) ) {
1115                      return false;
1116                 }
1117
1118                 if (! d.IsFileDependency()) {
1119                     return false;
1120                 }
1121             }
1122
1123             return true;
1124         }
1125  
1126         /// <summary>
1127         /// This method will return only the file dependencies from this dependency
1128         /// </summary>
1129         /// <returns></returns>
1130         public override string[] GetFileDependencies()
1131         {
1132             ArrayList fileNames = null;
1133             CacheDependency[] dependencies = null;
1134
1135             dependencies = GetDependencyArray();
1136             if (dependencies == null) {
1137                 return null;
1138             }
1139
1140             foreach (CacheDependency d in dependencies) {
1141                 // Check if the type is either CacheDependency or an Aggregate;
1142                 // for anything else, we can't guarantee it's a file only dependency.
1143                 if (object.ReferenceEquals(d.GetType(), typeof(CacheDependency))
1144                     || object.ReferenceEquals(d.GetType(), typeof(AggregateCacheDependency))) {
1145
1146                     string[] tmpFileNames = d.GetFileDependencies();
1147
1148                     if (tmpFileNames != null) {
1149
1150                         if (fileNames == null) {
1151                             fileNames = new ArrayList();
1152                         }
1153
1154                         fileNames.AddRange(tmpFileNames);
1155                     }
1156                 }
1157             }
1158
1159             if (fileNames != null) {
1160                 return (string[])fileNames.ToArray(typeof(string));
1161             }
1162             else {
1163                 return null;
1164             }
1165         }
1166     }
1167 }
1168