[runtime] Fixed get_process_module module name.
[mono.git] / mcs / class / System.IO.Compression / SharpCompress / Archive / AbstractWritableArchive.cs
1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using System.Linq;
5 using SharpCompress.Common;
6
7 namespace SharpCompress.Archive
8 {
9     internal abstract class AbstractWritableArchive<TEntry, TVolume> : AbstractArchive<TEntry, TVolume>
10         where TEntry : IArchiveEntry
11         where TVolume : IVolume
12     {
13         private readonly List<TEntry> newEntries = new List<TEntry>();
14         private readonly List<TEntry> removedEntries = new List<TEntry>();
15
16         private readonly List<TEntry> modifiedEntries = new List<TEntry>();
17         private bool hasModifications;
18
19         internal AbstractWritableArchive(ArchiveType type)
20             : base(type)
21         {
22         }
23
24         internal AbstractWritableArchive(ArchiveType type, Stream stream, Options options)
25             : base(type, stream.AsEnumerable(), options, null)
26         {
27         }
28
29 #if !PORTABLE && !NETFX_CORE
30         internal AbstractWritableArchive(ArchiveType type, FileInfo fileInfo, Options options)
31             : base(type, fileInfo, options, null)
32         {
33         }
34 #endif
35
36         public override ICollection<TEntry> Entries
37         {
38             get
39             {
40                 if (hasModifications)
41                 {
42                     return modifiedEntries;
43                 }
44                 return base.Entries;
45             }
46         }
47
48         private void RebuildModifiedCollection()
49         {
50             hasModifications = true;
51             newEntries.RemoveAll(v => removedEntries.Contains(v));
52             modifiedEntries.Clear();
53             modifiedEntries.AddRange(OldEntries.Concat(newEntries));
54         }
55
56         private IEnumerable<TEntry> OldEntries
57         {
58             get { return base.Entries.Where(x => !removedEntries.Contains(x)); }
59         }
60
61         public void RemoveEntry(TEntry entry)
62         {
63             if (!removedEntries.Contains(entry))
64             {
65                 removedEntries.Add(entry);
66                 RebuildModifiedCollection();
67             }
68         }
69
70         public TEntry AddEntry(string key, Stream source,
71                              long size = 0, DateTime? modified = null)
72         {
73             return AddEntry(key, source, false, size, modified);
74         }
75
76         public TEntry AddEntry(string key, Stream source, bool closeStream,
77                              long size = 0, DateTime? modified = null)
78         {
79             if (key.StartsWith("/")
80                 || key.StartsWith("\\"))
81             {
82                 key = key.Substring(1);
83             }
84             if (DoesKeyMatchExisting(key))
85             {
86                 throw new ArchiveException("Cannot add entry with duplicate key: " + key);
87             }
88             var entry = CreateEntry(key, source, size, modified, closeStream);
89             newEntries.Add(entry);
90             RebuildModifiedCollection();
91             return entry;
92         }
93
94         private bool DoesKeyMatchExisting(string key)
95         {
96             foreach (var path in Entries.Select(x => x.Key))
97             {
98                 var p = path.Replace('/','\\');
99                 if (p.StartsWith("\\"))
100                 {
101                     p = p.Substring(1);
102                 }
103                 return string.Equals(p, key, StringComparison.OrdinalIgnoreCase);
104             }
105             return false;
106         }
107
108         public void SaveTo(Stream stream, CompressionInfo compressionType)
109         {
110             //reset streams of new entries
111             newEntries.Cast<IWritableArchiveEntry>().ForEach(x => x.Stream.Seek(0, SeekOrigin.Begin));
112             SaveTo(stream, compressionType, OldEntries, newEntries);
113         }
114
115         protected TEntry CreateEntry(string key, Stream source, long size, DateTime? modified,
116             bool closeStream)
117         {
118             if (!source.CanRead || !source.CanSeek)
119             {
120                 throw new ArgumentException("Streams must be readable and seekable to use the Writing Archive API");
121             }
122             return CreateEntryInternal(key, source, size, modified, closeStream);
123         }
124
125         protected abstract TEntry CreateEntryInternal(string key, Stream source, long size, DateTime? modified,
126                                               bool closeStream);
127
128         protected abstract void SaveTo(Stream stream, CompressionInfo compressionType,
129                                        IEnumerable<TEntry> oldEntries, IEnumerable<TEntry> newEntries);
130
131         public override void Dispose()
132         {
133             base.Dispose();
134             newEntries.Cast<Entry>().ForEach(x => x.Close());
135             removedEntries.Cast<Entry>().ForEach(x => x.Close());
136             modifiedEntries.Cast<Entry>().ForEach(x => x.Close());
137         }
138     }
139 }