2 using System.Collections.Generic;
6 using SharpCompress.Common;
8 namespace SharpCompress.Archive
10 internal abstract class AbstractWritableArchive<TEntry, TVolume> : AbstractArchive<TEntry, TVolume>
11 where TEntry : IArchiveEntry
12 where TVolume : IVolume
14 private readonly List<TEntry> newEntries = new List<TEntry>();
15 private readonly List<TEntry> removedEntries = new List<TEntry>();
17 private readonly List<TEntry> modifiedEntries = new List<TEntry>();
18 private bool hasModifications;
20 internal AbstractWritableArchive(ArchiveType type)
25 internal AbstractWritableArchive(ArchiveType type, Stream stream, Options options)
26 : base(type, stream.AsEnumerable(), options, null)
30 #if !PORTABLE && !NETFX_CORE
31 internal AbstractWritableArchive(ArchiveType type, FileInfo fileInfo, Options options)
32 : base(type, fileInfo, options, null)
37 public override ICollection<TEntry> Entries
43 return modifiedEntries;
49 private void RebuildModifiedCollection()
51 hasModifications = true;
52 newEntries.RemoveAll(v => removedEntries.Contains(v));
53 modifiedEntries.Clear();
54 modifiedEntries.AddRange(OldEntries.Concat(newEntries));
57 private IEnumerable<TEntry> OldEntries
59 get { return base.Entries.Where(x => !removedEntries.Contains(x)); }
62 public void RemoveEntry(TEntry entry)
64 if (!removedEntries.Contains(entry))
66 removedEntries.Add(entry);
67 RebuildModifiedCollection();
71 public TEntry AddEntry(string key, Stream source,
72 long size = 0, DateTime? modified = null)
74 return AddEntry(key, source, false, size, modified);
77 public TEntry AddEntry(string key, Stream source, bool closeStream,
78 long size = 0, DateTime? modified = null)
80 if (key.StartsWith("/")
81 || key.StartsWith("\\"))
83 key = key.Substring(1);
85 // .NET allows duplicate entries when saving and loading Zip files.
86 // The following lines are disabled from upstream SharpCompress to allow this.
87 #if ZIP_ALLOW_DUPLICATE_KEYS
88 if (DoesKeyMatchExisting(key))
90 throw new ArchiveException("Cannot add entry with duplicate key: " + key);
93 var entry = CreateEntry(key, source, size, modified, closeStream);
94 newEntries.Add(entry);
95 RebuildModifiedCollection();
99 private bool DoesKeyMatchExisting(string key)
101 foreach (var path in Entries.Select(x => x.Key))
103 var p = path.Replace('/','\\');
104 if (p.StartsWith("\\"))
108 if (string.Equals(p, key, StringComparison.OrdinalIgnoreCase))
114 public void SaveTo(Stream stream, CompressionInfo compressionType, Encoding encoding = null)
116 //reset streams of new entries
117 newEntries.Cast<IWritableArchiveEntry>().ForEach(x => x.Stream.Seek(0, SeekOrigin.Begin));
118 SaveTo(stream, compressionType, encoding ?? ArchiveEncoding.Default, OldEntries, newEntries);
121 protected TEntry CreateEntry(string key, Stream source, long size, DateTime? modified,
124 if (!source.CanRead || !source.CanSeek)
126 throw new ArgumentException("Streams must be readable and seekable to use the Writing Archive API");
128 return CreateEntryInternal(key, source, size, modified, closeStream);
131 protected abstract TEntry CreateEntryInternal(string key, Stream source, long size, DateTime? modified,
134 protected abstract void SaveTo(Stream stream, CompressionInfo compressionType, Encoding encoding,
135 IEnumerable<TEntry> oldEntries, IEnumerable<TEntry> newEntries);
137 public override void Dispose()
140 newEntries.Cast<Entry>().ForEach(x => x.Close());
141 removedEntries.Cast<Entry>().ForEach(x => x.Close());
142 modifiedEntries.Cast<Entry>().ForEach(x => x.Close());