Merge pull request #2977 from BrzVlad/fix-major-log2
[mono.git] / mcs / class / referencesource / System.Web / Util / FileEnumerator.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="FileEnumerator.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>                                                                
5 //------------------------------------------------------------------------------
6
7 /*
8  * FileEnumerator class
9  * 
10  * Copyright (c) 2003 Microsoft Corporation
11  *
12  * Class to efficiently enumerate the files in a directory.  The only thing the framework provides
13  * to do this is Directory.GetFiles(), which is unusable on large directories because it returns an
14  * array containing all the file names at once (huge memory allocation).
15  *
16  * An efficient alternative is to use FindFirstFile/FindNextFile, which works but requires a lot
17  * more code.  Also, it makes our code base harder to port to non-windows platforms.
18  *
19  * This FileEnumerator class solves both problem, by providing a simple and efficient wrapper.
20  * By working with a single object, it is almost as efficient as calling FindFirstFile/FindNextFile,
21  * but is much easier to use.  e.g. instead of:
22  *
23  *      UnsafeNativeMethods.WIN32_FIND_DATA wfd;
24  *      IntPtr hFindFile = UnsafeNativeMethods.FindFirstFile(physicalDir + @"\*.*", out wfd);
25  *
26  *      if (hFindFile == INVALID_HANDLE_VALUE)
27  *          return;
28  *
29  *      try {
30  *          for (bool more=true; more; more=UnsafeNativeMethods.FindNextFile(hFindFile, out wfd)) {
31  *
32  *              // Skip false directories
33  *              if (wfd.cFileName == "." || wfd.cFileName == "..")
34  *                  continue;
35  *              
36  *              string fullPath = Path.Combine(physicalDir, wfd.cFileName);
37  *
38  *              ProcessFile(fullPath);
39  *          }
40  *      }
41  *      finally {
42  *          UnsafeNativeMethods.FindClose(hFindFile);
43  *      }
44  *
45  * we can simply write
46  *
47  *      foreach (FileData fileData in FileEnumerator.Create(physicalDir)) {
48  *          ProcessFile(fileData.FullName);
49  *      }
50  */
51
52
53 namespace System.Web.Util {
54
55 using System.IO;
56 using System.Collections;
57
58 /*
59  * This is a somewhat artificial base class for FileEnumerator.  The main reason
60  * for it is to allow user code to be more readable, by looking like:
61  *      foreach (FileData fileData in FileEnumerator.Create(path)) { ... }
62  * instead of
63  *      foreach (FileEnumerator fileData in FileEnumerator.Create(path)) { ... }
64  */
65 internal abstract class FileData {
66
67     protected string _path;
68     protected UnsafeNativeMethods.WIN32_FIND_DATA _wfd;
69
70     internal string Name {
71         get { return _wfd.cFileName; }
72     }
73
74     internal string FullName {
75         get { return _path + @"\" + _wfd.cFileName; }
76     }
77
78     internal bool IsDirectory {
79         get { return (_wfd.dwFileAttributes & UnsafeNativeMethods.FILE_ATTRIBUTE_DIRECTORY) != 0; }
80     }
81
82     internal bool IsHidden {
83         get { return (_wfd.dwFileAttributes & UnsafeNativeMethods.FILE_ATTRIBUTE_HIDDEN) != 0; }
84     }
85
86     internal FindFileData GetFindFileData() {
87         return new FindFileData(ref _wfd);
88     }
89 }
90
91 internal class FileEnumerator: FileData, IEnumerable, IEnumerator, IDisposable {
92     private IntPtr _hFindFile = UnsafeNativeMethods.INVALID_HANDLE_VALUE;
93
94     internal static FileEnumerator Create(string path) {
95         return new FileEnumerator(path);
96     }
97
98     private FileEnumerator(string path) {
99         _path = Path.GetFullPath(path);
100     }
101
102     ~FileEnumerator() {
103         ((IDisposable)this).Dispose();
104     }
105
106     // Should the current file be excluded from the enumeration
107     private bool SkipCurrent() {
108     
109         // Skip false directories
110         if (_wfd.cFileName == "." || _wfd.cFileName == "..")
111             return true;
112
113         return false;
114     }
115
116     // We just return ourselves for the enumerator, to avoid creating a new object
117     IEnumerator IEnumerable.GetEnumerator() {
118         return this;
119     }
120
121     bool IEnumerator.MoveNext() {
122
123         for (;;) {
124             if (_hFindFile == UnsafeNativeMethods.INVALID_HANDLE_VALUE) {
125                 _hFindFile = UnsafeNativeMethods.FindFirstFile(_path + @"\*.*", out _wfd);
126                 
127                 // Empty enumeration case
128                 if (_hFindFile == UnsafeNativeMethods.INVALID_HANDLE_VALUE)
129                     return false;
130             }
131             else {
132                 bool hasMoreFiles = UnsafeNativeMethods.FindNextFile(_hFindFile, out _wfd);
133                 if (!hasMoreFiles)
134                     return false;
135             }
136             
137             if (!SkipCurrent())
138                 return true;
139         }
140     }
141
142     // The current object of the enumeration is always ourselves.  No new object created.
143     object IEnumerator.Current {
144         get { return this; }
145     }
146
147     void IEnumerator.Reset() {
148         // We don't support reset, though it would be easy to add if needed
149         throw new InvalidOperationException();
150     }
151
152     void IDisposable.Dispose() {
153         if (_hFindFile != UnsafeNativeMethods.INVALID_HANDLE_VALUE) {
154             UnsafeNativeMethods.FindClose(_hFindFile);
155             _hFindFile = UnsafeNativeMethods.INVALID_HANDLE_VALUE;
156         }
157         System.GC.SuppressFinalize(this);
158     }
159 }
160
161 }