diff options
Diffstat (limited to '')
-rw-r--r-- | src/dtf/WixToolset.Dtf.Compression/ArchiveFileInfo.cs | 430 |
1 files changed, 430 insertions, 0 deletions
diff --git a/src/dtf/WixToolset.Dtf.Compression/ArchiveFileInfo.cs b/src/dtf/WixToolset.Dtf.Compression/ArchiveFileInfo.cs new file mode 100644 index 00000000..adcae3ec --- /dev/null +++ b/src/dtf/WixToolset.Dtf.Compression/ArchiveFileInfo.cs | |||
@@ -0,0 +1,430 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolset.Dtf.Compression | ||
4 | { | ||
5 | using System; | ||
6 | using System.IO; | ||
7 | using System.Runtime.Serialization; | ||
8 | using System.Diagnostics.CodeAnalysis; | ||
9 | |||
10 | /// <summary> | ||
11 | /// Abstract object representing a compressed file within an archive; | ||
12 | /// provides operations for getting the file properties and unpacking | ||
13 | /// the file. | ||
14 | /// </summary> | ||
15 | [Serializable] | ||
16 | public abstract class ArchiveFileInfo : FileSystemInfo | ||
17 | { | ||
18 | private ArchiveInfo archiveInfo; | ||
19 | private string name; | ||
20 | private string path; | ||
21 | |||
22 | private bool initialized; | ||
23 | private bool exists; | ||
24 | private int archiveNumber; | ||
25 | private FileAttributes attributes; | ||
26 | private DateTime lastWriteTime; | ||
27 | private long length; | ||
28 | |||
29 | /// <summary> | ||
30 | /// Creates a new ArchiveFileInfo object representing a file within | ||
31 | /// an archive in a specified path. | ||
32 | /// </summary> | ||
33 | /// <param name="archiveInfo">An object representing the archive | ||
34 | /// containing the file.</param> | ||
35 | /// <param name="filePath">The path to the file within the archive. | ||
36 | /// Usually, this is a simple file name, but if the archive contains | ||
37 | /// a directory structure this may include the directory.</param> | ||
38 | protected ArchiveFileInfo(ArchiveInfo archiveInfo, string filePath) | ||
39 | : base() | ||
40 | { | ||
41 | if (filePath == null) | ||
42 | { | ||
43 | throw new ArgumentNullException("filePath"); | ||
44 | } | ||
45 | |||
46 | this.Archive = archiveInfo; | ||
47 | |||
48 | this.name = System.IO.Path.GetFileName(filePath); | ||
49 | this.path = System.IO.Path.GetDirectoryName(filePath); | ||
50 | |||
51 | this.attributes = FileAttributes.Normal; | ||
52 | this.lastWriteTime = DateTime.MinValue; | ||
53 | } | ||
54 | |||
55 | /// <summary> | ||
56 | /// Creates a new ArchiveFileInfo object with all parameters specified; | ||
57 | /// used by subclasses when reading the metadata out of an archive. | ||
58 | /// </summary> | ||
59 | /// <param name="filePath">The internal path and name of the file in | ||
60 | /// the archive.</param> | ||
61 | /// <param name="archiveNumber">The archive number where the file | ||
62 | /// starts.</param> | ||
63 | /// <param name="attributes">The stored attributes of the file.</param> | ||
64 | /// <param name="lastWriteTime">The stored last write time of the | ||
65 | /// file.</param> | ||
66 | /// <param name="length">The uncompressed size of the file.</param> | ||
67 | protected ArchiveFileInfo( | ||
68 | string filePath, | ||
69 | int archiveNumber, | ||
70 | FileAttributes attributes, | ||
71 | DateTime lastWriteTime, | ||
72 | long length) | ||
73 | : this(null, filePath) | ||
74 | { | ||
75 | this.exists = true; | ||
76 | this.archiveNumber = archiveNumber; | ||
77 | this.attributes = attributes; | ||
78 | this.lastWriteTime = lastWriteTime; | ||
79 | this.length = length; | ||
80 | this.initialized = true; | ||
81 | } | ||
82 | |||
83 | /// <summary> | ||
84 | /// Initializes a new instance of the ArchiveFileInfo class with | ||
85 | /// serialized data. | ||
86 | /// </summary> | ||
87 | /// <param name="info">The SerializationInfo that holds the serialized | ||
88 | /// object data about the exception being thrown.</param> | ||
89 | /// <param name="context">The StreamingContext that contains contextual | ||
90 | /// information about the source or destination.</param> | ||
91 | protected ArchiveFileInfo(SerializationInfo info, StreamingContext context) | ||
92 | : base(info, context) | ||
93 | { | ||
94 | this.archiveInfo = (ArchiveInfo) info.GetValue( | ||
95 | "archiveInfo", typeof(ArchiveInfo)); | ||
96 | this.name = info.GetString("name"); | ||
97 | this.path = info.GetString("path"); | ||
98 | this.initialized = info.GetBoolean("initialized"); | ||
99 | this.exists = info.GetBoolean("exists"); | ||
100 | this.archiveNumber = info.GetInt32("archiveNumber"); | ||
101 | this.attributes = (FileAttributes) info.GetValue( | ||
102 | "attributes", typeof(FileAttributes)); | ||
103 | this.lastWriteTime = info.GetDateTime("lastWriteTime"); | ||
104 | this.length = info.GetInt64("length"); | ||
105 | } | ||
106 | |||
107 | /// <summary> | ||
108 | /// Gets the name of the file. | ||
109 | /// </summary> | ||
110 | /// <value>The name of the file, not including any path.</value> | ||
111 | public override string Name | ||
112 | { | ||
113 | get | ||
114 | { | ||
115 | return this.name; | ||
116 | } | ||
117 | } | ||
118 | |||
119 | /// <summary> | ||
120 | /// Gets the internal path of the file in the archive. | ||
121 | /// </summary> | ||
122 | /// <value>The internal path of the file in the archive, not including | ||
123 | /// the file name.</value> | ||
124 | public string Path | ||
125 | { | ||
126 | get | ||
127 | { | ||
128 | return this.path; | ||
129 | } | ||
130 | } | ||
131 | |||
132 | /// <summary> | ||
133 | /// Gets the full path to the file. | ||
134 | /// </summary> | ||
135 | /// <value>The full path to the file, including the full path to the | ||
136 | /// archive, the internal path in the archive, and the file name.</value> | ||
137 | /// <remarks> | ||
138 | /// For example, the path <c>"C:\archive.cab\file.txt"</c> refers to | ||
139 | /// a file "file.txt" inside the archive "archive.cab". | ||
140 | /// </remarks> | ||
141 | public override string FullName | ||
142 | { | ||
143 | get | ||
144 | { | ||
145 | string fullName = System.IO.Path.Combine(this.Path, this.Name); | ||
146 | |||
147 | if (this.Archive != null) | ||
148 | { | ||
149 | fullName = System.IO.Path.Combine(this.ArchiveName, fullName); | ||
150 | } | ||
151 | |||
152 | return fullName; | ||
153 | } | ||
154 | } | ||
155 | |||
156 | /// <summary> | ||
157 | /// Gets or sets the archive that contains this file. | ||
158 | /// </summary> | ||
159 | /// <value> | ||
160 | /// The ArchiveInfo instance that retrieved this file information -- this | ||
161 | /// may be null if the ArchiveFileInfo object was returned directly from | ||
162 | /// a stream. | ||
163 | /// </value> | ||
164 | public ArchiveInfo Archive | ||
165 | { | ||
166 | get | ||
167 | { | ||
168 | return (ArchiveInfo) this.archiveInfo; | ||
169 | } | ||
170 | |||
171 | internal set | ||
172 | { | ||
173 | this.archiveInfo = value; | ||
174 | |||
175 | // protected instance members inherited from FileSystemInfo: | ||
176 | this.OriginalPath = (value != null ? value.FullName : null); | ||
177 | this.FullPath = this.OriginalPath; | ||
178 | } | ||
179 | } | ||
180 | |||
181 | /// <summary> | ||
182 | /// Gets the full path of the archive that contains this file. | ||
183 | /// </summary> | ||
184 | /// <value>The full path of the archive that contains this file.</value> | ||
185 | public string ArchiveName | ||
186 | { | ||
187 | get | ||
188 | { | ||
189 | return this.Archive != null ? this.Archive.FullName : null; | ||
190 | } | ||
191 | } | ||
192 | |||
193 | /// <summary> | ||
194 | /// Gets the number of the archive where this file starts. | ||
195 | /// </summary> | ||
196 | /// <value>The number of the archive where this file starts.</value> | ||
197 | /// <remarks>A single archive or the first archive in a chain is | ||
198 | /// numbered 0.</remarks> | ||
199 | public int ArchiveNumber | ||
200 | { | ||
201 | get | ||
202 | { | ||
203 | return this.archiveNumber; | ||
204 | } | ||
205 | } | ||
206 | |||
207 | /// <summary> | ||
208 | /// Checks if the file exists within the archive. | ||
209 | /// </summary> | ||
210 | /// <value>True if the file exists, false otherwise.</value> | ||
211 | public override bool Exists | ||
212 | { | ||
213 | get | ||
214 | { | ||
215 | if (!this.initialized) | ||
216 | { | ||
217 | this.Refresh(); | ||
218 | } | ||
219 | |||
220 | return this.exists; | ||
221 | } | ||
222 | } | ||
223 | |||
224 | /// <summary> | ||
225 | /// Gets the uncompressed size of the file. | ||
226 | /// </summary> | ||
227 | /// <value>The uncompressed size of the file in bytes.</value> | ||
228 | public long Length | ||
229 | { | ||
230 | get | ||
231 | { | ||
232 | if (!this.initialized) | ||
233 | { | ||
234 | this.Refresh(); | ||
235 | } | ||
236 | |||
237 | return this.length; | ||
238 | } | ||
239 | } | ||
240 | |||
241 | /// <summary> | ||
242 | /// Gets the attributes of the file. | ||
243 | /// </summary> | ||
244 | /// <value>The attributes of the file as stored in the archive.</value> | ||
245 | public new FileAttributes Attributes | ||
246 | { | ||
247 | get | ||
248 | { | ||
249 | if (!this.initialized) | ||
250 | { | ||
251 | this.Refresh(); | ||
252 | } | ||
253 | |||
254 | return this.attributes; | ||
255 | } | ||
256 | } | ||
257 | |||
258 | /// <summary> | ||
259 | /// Gets the last modification time of the file. | ||
260 | /// </summary> | ||
261 | /// <value>The last modification time of the file as stored in the | ||
262 | /// archive.</value> | ||
263 | public new DateTime LastWriteTime | ||
264 | { | ||
265 | get | ||
266 | { | ||
267 | if (!this.initialized) | ||
268 | { | ||
269 | this.Refresh(); | ||
270 | } | ||
271 | |||
272 | return this.lastWriteTime; | ||
273 | } | ||
274 | } | ||
275 | |||
276 | /// <summary> | ||
277 | /// Sets the SerializationInfo with information about the archive. | ||
278 | /// </summary> | ||
279 | /// <param name="info">The SerializationInfo that holds the serialized | ||
280 | /// object data.</param> | ||
281 | /// <param name="context">The StreamingContext that contains contextual | ||
282 | /// information about the source or destination.</param> | ||
283 | public override void GetObjectData( | ||
284 | SerializationInfo info, StreamingContext context) | ||
285 | { | ||
286 | base.GetObjectData(info, context); | ||
287 | info.AddValue("archiveInfo", this.archiveInfo); | ||
288 | info.AddValue("name", this.name); | ||
289 | info.AddValue("path", this.path); | ||
290 | info.AddValue("initialized", this.initialized); | ||
291 | info.AddValue("exists", this.exists); | ||
292 | info.AddValue("archiveNumber", this.archiveNumber); | ||
293 | info.AddValue("attributes", this.attributes); | ||
294 | info.AddValue("lastWriteTime", this.lastWriteTime); | ||
295 | info.AddValue("length", this.length); | ||
296 | } | ||
297 | |||
298 | /// <summary> | ||
299 | /// Gets the full path to the file. | ||
300 | /// </summary> | ||
301 | /// <returns>The same as <see cref="FullName"/></returns> | ||
302 | public override string ToString() | ||
303 | { | ||
304 | return this.FullName; | ||
305 | } | ||
306 | |||
307 | /// <summary> | ||
308 | /// Deletes the file. NOT SUPPORTED. | ||
309 | /// </summary> | ||
310 | /// <exception cref="NotSupportedException">Files cannot be deleted | ||
311 | /// from an existing archive.</exception> | ||
312 | public override void Delete() | ||
313 | { | ||
314 | throw new NotSupportedException(); | ||
315 | } | ||
316 | |||
317 | /// <summary> | ||
318 | /// Refreshes the attributes and other cached information about the file, | ||
319 | /// by re-reading the information from the archive. | ||
320 | /// </summary> | ||
321 | public new void Refresh() | ||
322 | { | ||
323 | base.Refresh(); | ||
324 | |||
325 | if (this.Archive != null) | ||
326 | { | ||
327 | string filePath = System.IO.Path.Combine(this.Path, this.Name); | ||
328 | ArchiveFileInfo updatedFile = this.Archive.GetFile(filePath); | ||
329 | if (updatedFile == null) | ||
330 | { | ||
331 | throw new FileNotFoundException( | ||
332 | "File not found in archive.", filePath); | ||
333 | } | ||
334 | |||
335 | this.Refresh(updatedFile); | ||
336 | } | ||
337 | } | ||
338 | |||
339 | /// <summary> | ||
340 | /// Extracts the file. | ||
341 | /// </summary> | ||
342 | /// <param name="destFileName">The destination path where the file | ||
343 | /// will be extracted.</param> | ||
344 | [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "dest")] | ||
345 | public void CopyTo(string destFileName) | ||
346 | { | ||
347 | this.CopyTo(destFileName, false); | ||
348 | } | ||
349 | |||
350 | /// <summary> | ||
351 | /// Extracts the file, optionally overwriting any existing file. | ||
352 | /// </summary> | ||
353 | /// <param name="destFileName">The destination path where the file | ||
354 | /// will be extracted.</param> | ||
355 | /// <param name="overwrite">If true, <paramref name="destFileName"/> | ||
356 | /// will be overwritten if it exists.</param> | ||
357 | /// <exception cref="IOException"><paramref name="overwrite"/> is false | ||
358 | /// and <paramref name="destFileName"/> exists.</exception> | ||
359 | [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "dest")] | ||
360 | public void CopyTo(string destFileName, bool overwrite) | ||
361 | { | ||
362 | if (destFileName == null) | ||
363 | { | ||
364 | throw new ArgumentNullException("destFileName"); | ||
365 | } | ||
366 | |||
367 | if (!overwrite && File.Exists(destFileName)) | ||
368 | { | ||
369 | throw new IOException(); | ||
370 | } | ||
371 | |||
372 | if (this.Archive == null) | ||
373 | { | ||
374 | throw new InvalidOperationException(); | ||
375 | } | ||
376 | |||
377 | this.Archive.UnpackFile( | ||
378 | System.IO.Path.Combine(this.Path, this.Name), destFileName); | ||
379 | } | ||
380 | |||
381 | /// <summary> | ||
382 | /// Opens the archive file for reading without actually extracting the | ||
383 | /// file to disk. | ||
384 | /// </summary> | ||
385 | /// <returns> | ||
386 | /// A stream for reading directly from the packed file. Like any stream | ||
387 | /// this should be closed/disposed as soon as it is no longer needed. | ||
388 | /// </returns> | ||
389 | public Stream OpenRead() | ||
390 | { | ||
391 | return this.Archive.OpenRead(System.IO.Path.Combine(this.Path, this.Name)); | ||
392 | } | ||
393 | |||
394 | /// <summary> | ||
395 | /// Opens the archive file reading text with UTF-8 encoding without | ||
396 | /// actually extracting the file to disk. | ||
397 | /// </summary> | ||
398 | /// <returns> | ||
399 | /// A reader for reading text directly from the packed file. Like any reader | ||
400 | /// this should be closed/disposed as soon as it is no longer needed. | ||
401 | /// </returns> | ||
402 | /// <remarks> | ||
403 | /// To open an archived text file with different encoding, use the | ||
404 | /// <see cref="OpenRead" /> method and pass the returned stream to one of | ||
405 | /// the <see cref="StreamReader" /> constructor overloads. | ||
406 | /// </remarks> | ||
407 | public StreamReader OpenText() | ||
408 | { | ||
409 | return this.Archive.OpenText(System.IO.Path.Combine(this.Path, this.Name)); | ||
410 | } | ||
411 | |||
412 | /// <summary> | ||
413 | /// Refreshes the information in this object with new data retrieved | ||
414 | /// from an archive. | ||
415 | /// </summary> | ||
416 | /// <param name="newFileInfo">Fresh instance for the same file just | ||
417 | /// read from the archive.</param> | ||
418 | /// <remarks> | ||
419 | /// Subclasses may override this method to refresh sublcass fields. | ||
420 | /// However they should always call the base implementation first. | ||
421 | /// </remarks> | ||
422 | protected virtual void Refresh(ArchiveFileInfo newFileInfo) | ||
423 | { | ||
424 | this.exists = newFileInfo.exists; | ||
425 | this.length = newFileInfo.length; | ||
426 | this.attributes = newFileInfo.attributes; | ||
427 | this.lastWriteTime = newFileInfo.lastWriteTime; | ||
428 | } | ||
429 | } | ||
430 | } | ||