mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-07-13 20:46:47 +00:00
195 lines
5.1 KiB
C#
195 lines
5.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using SharpCompress.Common;
|
|
using SharpCompress.IO;
|
|
using SharpCompress.Writers;
|
|
|
|
namespace SharpCompress.Archives;
|
|
|
|
public abstract class AbstractWritableArchive<TEntry, TVolume>
|
|
: AbstractArchive<TEntry, TVolume>,
|
|
IWritableArchive
|
|
where TEntry : IArchiveEntry
|
|
where TVolume : IVolume
|
|
{
|
|
private class RebuildPauseDisposable : IDisposable
|
|
{
|
|
private readonly AbstractWritableArchive<TEntry, TVolume> archive;
|
|
|
|
public RebuildPauseDisposable(AbstractWritableArchive<TEntry, TVolume> archive)
|
|
{
|
|
this.archive = archive;
|
|
archive.pauseRebuilding = true;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
archive.pauseRebuilding = false;
|
|
archive.RebuildModifiedCollection();
|
|
}
|
|
}
|
|
|
|
private readonly List<TEntry> newEntries = new List<TEntry>();
|
|
private readonly List<TEntry> removedEntries = new List<TEntry>();
|
|
|
|
private readonly List<TEntry> modifiedEntries = new List<TEntry>();
|
|
private bool hasModifications;
|
|
private bool pauseRebuilding;
|
|
|
|
internal AbstractWritableArchive(ArchiveType type) : base(type) { }
|
|
|
|
internal AbstractWritableArchive(ArchiveType type, SourceStream srcStream)
|
|
: base(type, srcStream) { }
|
|
|
|
public override ICollection<TEntry> Entries
|
|
{
|
|
get
|
|
{
|
|
if (hasModifications)
|
|
{
|
|
return modifiedEntries;
|
|
}
|
|
return base.Entries;
|
|
}
|
|
}
|
|
|
|
public IDisposable PauseEntryRebuilding()
|
|
{
|
|
return new RebuildPauseDisposable(this);
|
|
}
|
|
|
|
private void RebuildModifiedCollection()
|
|
{
|
|
if (pauseRebuilding)
|
|
{
|
|
return;
|
|
}
|
|
hasModifications = true;
|
|
newEntries.RemoveAll(v => removedEntries.Contains(v));
|
|
modifiedEntries.Clear();
|
|
modifiedEntries.AddRange(OldEntries.Concat(newEntries));
|
|
}
|
|
|
|
private IEnumerable<TEntry> OldEntries
|
|
{
|
|
get { return base.Entries.Where(x => !removedEntries.Contains(x)); }
|
|
}
|
|
|
|
public void RemoveEntry(TEntry entry)
|
|
{
|
|
if (!removedEntries.Contains(entry))
|
|
{
|
|
removedEntries.Add(entry);
|
|
RebuildModifiedCollection();
|
|
}
|
|
}
|
|
|
|
void IWritableArchive.RemoveEntry(IArchiveEntry entry)
|
|
{
|
|
RemoveEntry((TEntry)entry);
|
|
}
|
|
|
|
public TEntry AddEntry(string key, Stream source, long size = 0, DateTime? modified = null)
|
|
{
|
|
return AddEntry(key, source, false, size, modified);
|
|
}
|
|
|
|
IArchiveEntry IWritableArchive.AddEntry(
|
|
string key,
|
|
Stream source,
|
|
bool closeStream,
|
|
long size,
|
|
DateTime? modified
|
|
)
|
|
{
|
|
return AddEntry(key, source, closeStream, size, modified);
|
|
}
|
|
|
|
public TEntry AddEntry(
|
|
string key,
|
|
Stream source,
|
|
bool closeStream,
|
|
long size = 0,
|
|
DateTime? modified = null
|
|
)
|
|
{
|
|
if (key.Length > 0 && key[0] is '/' or '\\')
|
|
{
|
|
key = key.Substring(1);
|
|
}
|
|
if (DoesKeyMatchExisting(key))
|
|
{
|
|
throw new ArchiveException("Cannot add entry with duplicate key: " + key);
|
|
}
|
|
var entry = CreateEntry(key, source, size, modified, closeStream);
|
|
newEntries.Add(entry);
|
|
RebuildModifiedCollection();
|
|
return entry;
|
|
}
|
|
|
|
private bool DoesKeyMatchExisting(string key)
|
|
{
|
|
foreach (var path in Entries.Select(x => x.Key))
|
|
{
|
|
var p = path.Replace('/', '\\');
|
|
if (p.Length > 0 && p[0] == '\\')
|
|
{
|
|
p = p.Substring(1);
|
|
}
|
|
return string.Equals(p, key, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public void SaveTo(Stream stream, WriterOptions options)
|
|
{
|
|
//reset streams of new entries
|
|
newEntries
|
|
.Cast<IWritableArchiveEntry>()
|
|
.ForEach(x => x.Stream.Seek(0, SeekOrigin.Begin));
|
|
SaveTo(stream, options, OldEntries, newEntries);
|
|
}
|
|
|
|
protected TEntry CreateEntry(
|
|
string key,
|
|
Stream source,
|
|
long size,
|
|
DateTime? modified,
|
|
bool closeStream
|
|
)
|
|
{
|
|
if (!source.CanRead || !source.CanSeek)
|
|
{
|
|
throw new ArgumentException(
|
|
"Streams must be readable and seekable to use the Writing Archive API"
|
|
);
|
|
}
|
|
return CreateEntryInternal(key, source, size, modified, closeStream);
|
|
}
|
|
|
|
protected abstract TEntry CreateEntryInternal(
|
|
string key,
|
|
Stream source,
|
|
long size,
|
|
DateTime? modified,
|
|
bool closeStream
|
|
);
|
|
|
|
protected abstract void SaveTo(
|
|
Stream stream,
|
|
WriterOptions options,
|
|
IEnumerable<TEntry> oldEntries,
|
|
IEnumerable<TEntry> newEntries
|
|
);
|
|
|
|
public override void Dispose()
|
|
{
|
|
base.Dispose();
|
|
newEntries.Cast<Entry>().ForEach(x => x.Close());
|
|
removedEntries.Cast<Entry>().ForEach(x => x.Close());
|
|
modifiedEntries.Cast<Entry>().ForEach(x => x.Close());
|
|
}
|
|
}
|