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