-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathDeferredCreationArchiveBase.cs
154 lines (96 loc) · 4.7 KB
/
DeferredCreationArchiveBase.cs
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
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Gsemac.IO.Compression {
internal abstract class DeferredCreationArchiveBase :
ArchiveBase {
// Public members
public override IArchiveEntry AddEntry(Stream stream, string entryName, IArchiveEntryOptions options) {
if (!CanWrite)
throw new InvalidOperationException(Properties.ExceptionMessages.ArchiveIsReadOnly);
if (stream is null)
throw new ArgumentNullException(nameof(stream));
if (stream is FileStream fileStream) {
if (options is null)
options = ArchiveEntryOptions.Default;
IArchiveEntry existingEntry = GetEntry(entryName);
NewArchiveEntry newEntry = new NewArchiveEntry() {
Name = SanitizeEntryName(entryName),
LastModified = DateTimeOffset.Now,
Comment = options.Comment,
FilePath = Path.GetFullPath(fileStream.Name),
};
if (existingEntry is object) {
if (!options.Overwrite) {
throw new ArchiveEntryExistsException();
}
else if (newEntry.RenameRequired) {
// While 7-Zip/WinRAR will happily overwrite old entries when adding new entries to the archive with the same name, it will
// not delete an older entry when renaming a new entry to the same name. Instead, it creates a duplicate.
// We need to make sure that we delete the old entry to avoid creating a duplicate.
DeleteEntry(existingEntry);
}
}
newEntries.Add(newEntry);
if (!options.LeaveStreamOpen)
stream.Close();
return newEntries.Last();
}
else {
throw new ArgumentException(Properties.ExceptionMessages.ArchiveOnlySupportsFileStreams);
}
}
public override void DeleteEntry(IArchiveEntry entry) {
if (!CanWrite)
throw new InvalidOperationException(Properties.ExceptionMessages.ArchiveIsReadOnly);
if (entry is null)
throw new ArgumentNullException(nameof(entry));
bool entryRemoved = false;
// If this is an entry that we added previously, remove it.
if (entry is NewArchiveEntry newArchiveEntry)
entryRemoved = newEntries.Remove(newArchiveEntry);
// If this is an existing entry, remove it.
if (existingEntries.Value.Contains(entry)) {
deletedEntries.Add(entry);
entryRemoved = true;
}
if (!entryRemoved)
throw new ArchiveEntryDoesNotExistException();
}
public override IEnumerable<IArchiveEntry> GetEntries() {
if (!CanRead)
throw new InvalidOperationException(Properties.ExceptionMessages.ArchiveIsWriteOnly);
// Include all existing and new entries minus deleted and overwritten entries.
return existingEntries.Value
.Concat(newEntries)
.Except(deletedEntries)
.Except(existingEntries.Value.Where(entry => newEntries.Any(newEntry => newEntry.Name.Equals(entry.Name))));
}
public override void Close() {
if (!archiveIsClosed)
CommitChanges(newEntries, deletedEntries);
archiveIsClosed = true;
}
// Internal members
protected class NewArchiveEntry :
GenericArchiveEntry {
public string FilePath { get; set; }
public bool RenameRequired => !Name.Equals(PathUtilities.GetFileName(FilePath));
}
// Protected members
protected DeferredCreationArchiveBase() {
}
protected DeferredCreationArchiveBase(IArchiveOptions archiveOptions) :
base(archiveOptions) {
existingEntries = new Lazy<List<IArchiveEntry>>(ReadArchiveEntries);
}
protected abstract List<IArchiveEntry> ReadArchiveEntries();
protected abstract void CommitChanges(IEnumerable<NewArchiveEntry> newEntries, IEnumerable<IArchiveEntry> deletedEntries);
// Private members
private readonly Lazy<List<IArchiveEntry>> existingEntries;
private readonly List<NewArchiveEntry> newEntries = new List<NewArchiveEntry>();
private readonly List<IArchiveEntry> deletedEntries = new List<IArchiveEntry>();
private bool archiveIsClosed = false;
}
}