aboutsummaryrefslogtreecommitdiff
path: root/src/dtf/WixToolset.Dtf.Compression/ArchiveFileInfo.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/dtf/WixToolset.Dtf.Compression/ArchiveFileInfo.cs430
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
3namespace 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}