From 8775b65f588f300e9830970d59e392557cadf9bb Mon Sep 17 00:00:00 2001 From: vpenades <5433822+vpenades@users.noreply.github.com> Date: Tue, 29 Nov 2022 16:45:20 +0100 Subject: [PATCH 1/6] Generalized factories to readers and writers. --- src/SharpCompress/Archives/ArchiveFactory.cs | 82 ++-------- .../Archives/GZip/GZipArchiveFactory.cs | 51 ------ src/SharpCompress/Archives/IArchiveFactory.cs | 31 +--- .../Archives/Tar/TarArchiveFactory.cs | 52 ------ .../Archives/Zip/ZipArchiveFactory.cs | 62 -------- src/SharpCompress/Factories/Factory.cs | 86 ++++++++++ src/SharpCompress/Factories/GZipFactory.cs | 121 ++++++++++++++ src/SharpCompress/Factories/IFactory.cs | 45 ++++++ .../RarFactory.cs} | 39 ++++- .../SevenZipFactory.cs} | 38 ++++- src/SharpCompress/Factories/TarFactory.cs | 149 ++++++++++++++++++ src/SharpCompress/Factories/ZipFactory.cs | 112 +++++++++++++ src/SharpCompress/Readers/IReaderFactory.cs | 23 +++ src/SharpCompress/Readers/ReaderFactory.cs | 82 +--------- src/SharpCompress/Writers/IWriterFactory.cs | 16 ++ src/SharpCompress/Writers/WriterFactory.cs | 41 ++--- 16 files changed, 658 insertions(+), 372 deletions(-) delete mode 100644 src/SharpCompress/Archives/GZip/GZipArchiveFactory.cs delete mode 100644 src/SharpCompress/Archives/Tar/TarArchiveFactory.cs delete mode 100644 src/SharpCompress/Archives/Zip/ZipArchiveFactory.cs create mode 100644 src/SharpCompress/Factories/Factory.cs create mode 100644 src/SharpCompress/Factories/GZipFactory.cs create mode 100644 src/SharpCompress/Factories/IFactory.cs rename src/SharpCompress/{Archives/Rar/RarArchiveFactory.cs => Factories/RarFactory.cs} (56%) rename src/SharpCompress/{Archives/SevenZip/SevenZipArchiveFactory.cs => Factories/SevenZipFactory.cs} (56%) create mode 100644 src/SharpCompress/Factories/TarFactory.cs create mode 100644 src/SharpCompress/Factories/ZipFactory.cs create mode 100644 src/SharpCompress/Readers/IReaderFactory.cs create mode 100644 src/SharpCompress/Writers/IWriterFactory.cs diff --git a/src/SharpCompress/Archives/ArchiveFactory.cs b/src/SharpCompress/Archives/ArchiveFactory.cs index 8bb8f3f3..9de21b07 100644 --- a/src/SharpCompress/Archives/ArchiveFactory.cs +++ b/src/SharpCompress/Archives/ArchiveFactory.cs @@ -15,36 +15,6 @@ namespace SharpCompress.Archives { public static class ArchiveFactory { - private static readonly HashSet archiveFactories; - - /// - /// Gets the collection of registered archive factories - /// - public static IReadOnlyCollection Factories => archiveFactories; - - static ArchiveFactory() - { - archiveFactories = new HashSet(); - - RegisterFactory(new Zip.ZipArchiveFactory()); - RegisterFactory(new Rar.RarArchiveFactory()); - RegisterFactory(new SevenZip.SevenZipArchiveFactory()); - RegisterFactory(new GZip.GZipArchiveFactory()); - RegisterFactory(new Tar.TarArchiveFactory()); - } - - /// - /// Registers an archive factory. - /// - /// The factory to register. - /// must not be null. - public static void RegisterFactory(IArchiveFactory factory) - { - factory.CheckNotNull(nameof(factory)); - - archiveFactories.Add(factory); - } - /// /// Opens an Archive for random access /// @@ -171,9 +141,11 @@ namespace SharpCompress.Archives throw new ArgumentException("Stream should be readable and seekable"); } + var factores = Factories.Factory.Factories.OfType(); + long startPosition = stream.Position; - foreach (var factory in Factories) + foreach (var factory in factores) { stream.Seek(startPosition, SeekOrigin.Begin); @@ -185,9 +157,7 @@ namespace SharpCompress.Archives } } - var extensions = Factories - .Select(item => item.Name) - .Aggregate((a, b) => a + ", " + b); + var extensions = string.Join(", ", factores.Select(item => item.Name)); throw new InvalidOperationException($"Cannot determine compressed stream type. Supported Archive Formats: {extensions}"); } @@ -208,41 +178,21 @@ namespace SharpCompress.Archives { throw new ArgumentException("Stream should be readable and seekable"); } - if (ZipArchive.IsZipFile(stream, null)) - type = ArchiveType.Zip; - stream.Seek(0, SeekOrigin.Begin); - if (type == null) + + var startPosition = stream.Position; + + foreach(var factory in Factories.Factory.Factories) { - if (SevenZipArchive.IsSevenZipFile(stream)) - type = ArchiveType.SevenZip; - stream.Seek(0, SeekOrigin.Begin); - } - if (type == null) - { - if (GZipArchive.IsGZipFile(stream)) - type = ArchiveType.GZip; - stream.Seek(0, SeekOrigin.Begin); - } - if (type == null) - { - if (RarArchive.IsRarFile(stream)) - type = ArchiveType.Rar; - stream.Seek(0, SeekOrigin.Begin); - } - if (type == null) - { - if (TarArchive.IsTarFile(stream)) - type = ArchiveType.Tar; - stream.Seek(0, SeekOrigin.Begin); - } - if (type == null) //test multipartzip as it could find zips in other non compressed archive types? - { - if (ZipArchive.IsZipMulti(stream)) //test the zip (last) file of a multipart zip - type = ArchiveType.Zip; - stream.Seek(0, SeekOrigin.Begin); + stream.Position = startPosition; + + if (factory.IsArchive(stream, null)) + { + type = factory.KnownArchiveType; + return true; + } } - return type != null; + return false; } /// diff --git a/src/SharpCompress/Archives/GZip/GZipArchiveFactory.cs b/src/SharpCompress/Archives/GZip/GZipArchiveFactory.cs deleted file mode 100644 index 0d52d6ca..00000000 --- a/src/SharpCompress/Archives/GZip/GZipArchiveFactory.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -using SharpCompress.Readers; - -namespace SharpCompress.Archives.GZip -{ - public class GZipArchiveFactory : IArchiveFactory - { - /// - public string Name => "GZip"; - - /// - public IEnumerable GetSupportedExtensions() - { - yield return "gz"; - } - - /// - public bool IsArchive(Stream stream, string? password = null) - { - return GZipArchive.IsGZipFile(stream); - } - - /// - public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) - { - return GZipArchive.Open(stream, readerOptions); - } - - /// - public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) - { - return GZipArchive.Open(fileInfo, readerOptions); - } - - /// - public IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) - { - return GZipArchive.Open(streams, readerOptions); - } - - /// - public IArchive Open(IEnumerable fileInfos, ReaderOptions? readerOptions = null) - { - return GZipArchive.Open(fileInfos, readerOptions); - } - } -} diff --git a/src/SharpCompress/Archives/IArchiveFactory.cs b/src/SharpCompress/Archives/IArchiveFactory.cs index 5674307d..0bf43638 100644 --- a/src/SharpCompress/Archives/IArchiveFactory.cs +++ b/src/SharpCompress/Archives/IArchiveFactory.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using SharpCompress.Common; +using SharpCompress.Factories; using SharpCompress.Readers; namespace SharpCompress.Archives @@ -13,32 +14,16 @@ namespace SharpCompress.Archives /// /// Currently implemented by:
/// - /// - /// - /// - /// - /// + /// + /// + /// + /// + /// /// ///
- public interface IArchiveFactory + public interface IArchiveFactory : IFactory { - /// - /// Gets the archive Type name - /// - string Name { get; } - - /// - /// returns the extensions typically used by this archive type. - /// - /// - IEnumerable GetSupportedExtensions(); - - /// - /// Returns true if the stream represents an archive of the format defined by this type. - /// - /// - /// /// optional password - bool IsArchive(Stream stream, string? password = null); + /// /// Opens an Archive for random access. diff --git a/src/SharpCompress/Archives/Tar/TarArchiveFactory.cs b/src/SharpCompress/Archives/Tar/TarArchiveFactory.cs deleted file mode 100644 index fb9c170c..00000000 --- a/src/SharpCompress/Archives/Tar/TarArchiveFactory.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -using SharpCompress.Readers; - -namespace SharpCompress.Archives.Tar -{ - public class TarArchiveFactory : IArchiveFactory - { - /// - public string Name => "Tar"; - - /// - public IEnumerable GetSupportedExtensions() - { - yield return "tar"; - } - - /// - public bool IsArchive(Stream stream, string? password = null) - { - return TarArchive.IsTarFile(stream); - } - - /// - public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) - { - return TarArchive.Open(stream, readerOptions); - } - - /// - public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) - { - return TarArchive.Open(fileInfo, readerOptions); - } - - /// - public IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) - { - return TarArchive.Open(streams, readerOptions); - } - - /// - public IArchive Open(IEnumerable fileInfos, ReaderOptions? readerOptions = null) - { - return TarArchive.Open(fileInfos, readerOptions); - } - - } -} diff --git a/src/SharpCompress/Archives/Zip/ZipArchiveFactory.cs b/src/SharpCompress/Archives/Zip/ZipArchiveFactory.cs deleted file mode 100644 index 68c3f2b6..00000000 --- a/src/SharpCompress/Archives/Zip/ZipArchiveFactory.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -using SharpCompress.Readers; - -namespace SharpCompress.Archives.Zip -{ - public class ZipArchiveFactory : IArchiveFactory - { - /// - public string Name => "Zip"; - - /// - public IEnumerable GetSupportedExtensions() - { - yield return "zip"; - yield return "zipx"; - yield return "cbz"; - } - - /// - public bool IsArchive(Stream stream, string? password = null) - { - long startPosition = stream.Position; - - if (ZipArchive.IsZipFile(stream, password)) return true; - - stream.Seek(startPosition, SeekOrigin.Begin); - - //test the zip (last) file of a multipart zip - if (ZipArchive.IsZipMulti(stream)) return true; - - return false; - } - - /// - public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) - { - return ZipArchive.Open(stream, readerOptions); - } - - /// - public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) - { - return ZipArchive.Open(fileInfo, readerOptions); - } - - /// - public IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) - { - return ZipArchive.Open(streams, readerOptions); - } - - /// - public IArchive Open(IEnumerable fileInfos, ReaderOptions? readerOptions = null) - { - return ZipArchive.Open(fileInfos, readerOptions); - } - } -} diff --git a/src/SharpCompress/Factories/Factory.cs b/src/SharpCompress/Factories/Factory.cs new file mode 100644 index 00000000..dd4ede80 --- /dev/null +++ b/src/SharpCompress/Factories/Factory.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using SharpCompress.Archives; +using SharpCompress.Common; +using SharpCompress.IO; +using SharpCompress.Readers; + +namespace SharpCompress.Factories +{ + /// + public abstract class Factory : IFactory + { + static Factory() + { + RegisterFactory(new ZipFactory()); + RegisterFactory(new RarFactory()); + RegisterFactory(new SevenZipFactory()); + RegisterFactory(new GZipFactory()); + RegisterFactory(new TarFactory()); + } + + private static readonly HashSet _factories = new HashSet(); + + /// + /// Gets the collection of registered . + /// + public static IEnumerable Factories => _factories; + + /// + /// Registers an archive factory. + /// + /// The factory to register. + /// must not be null. + public static void RegisterFactory(Factory factory) + { + factory.CheckNotNull(nameof(factory)); + + _factories.Add(factory); + } + + /// + public abstract string Name { get; } + + /// + public virtual ArchiveType? KnownArchiveType => null; + + /// + public abstract IEnumerable GetSupportedExtensions(); + + /// + public abstract bool IsArchive(Stream stream, string? password = null); + + /// + /// Tries to open an from a . + /// + /// + /// This method provides extra insight to support loading compressed TAR files. + /// + /// + /// + /// + /// + internal virtual bool TryOpenReader(RewindableStream rewindableStream, ReaderOptions options, out IReader? reader) + { + reader = null; + + if (this is IReaderFactory readerFactory) + { + rewindableStream.Rewind(false); + if (this.IsArchive(rewindableStream, options.Password)) + { + rewindableStream.Rewind(true); + reader = readerFactory.OpenReader(rewindableStream, options); + return true; + } + } + + return false; + } + } +} diff --git a/src/SharpCompress/Factories/GZipFactory.cs b/src/SharpCompress/Factories/GZipFactory.cs new file mode 100644 index 00000000..6e9fb6a8 --- /dev/null +++ b/src/SharpCompress/Factories/GZipFactory.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; + +using SharpCompress.Archives; +using SharpCompress.Archives.GZip; +using SharpCompress.Archives.Tar; +using SharpCompress.Common; +using SharpCompress.IO; +using SharpCompress.Readers; +using SharpCompress.Readers.GZip; +using SharpCompress.Readers.Tar; +using SharpCompress.Writers; +using SharpCompress.Writers.GZip; + +namespace SharpCompress.Factories +{ + public class GZipFactory : Factory, IArchiveFactory, IReaderFactory, IWriterFactory + { + #region IFactory + + /// + public override string Name => "GZip"; + + /// + public override ArchiveType? KnownArchiveType => ArchiveType.GZip; + + /// + public override IEnumerable GetSupportedExtensions() + { + yield return "gz"; + } + + /// + public override bool IsArchive(Stream stream, string? password = null) + { + return GZipArchive.IsGZipFile(stream); + } + + #endregion + + #region IArchiveFactory + + /// + public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) + { + return GZipArchive.Open(stream, readerOptions); + } + + /// + public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) + { + return GZipArchive.Open(fileInfo, readerOptions); + } + + /// + public IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) + { + return GZipArchive.Open(streams, readerOptions); + } + + /// + public IArchive Open(IEnumerable fileInfos, ReaderOptions? readerOptions = null) + { + return GZipArchive.Open(fileInfos, readerOptions); + } + + #endregion + + #region IReaderFactory + + /// + internal override bool TryOpenReader(RewindableStream rewindableStream, ReaderOptions options, out IReader? reader) + { + reader = null; + + rewindableStream.Rewind(false); + if (GZipArchive.IsGZipFile(rewindableStream)) + { + rewindableStream.Rewind(false); + var testStream = new GZipStream(rewindableStream, CompressionMode.Decompress); + if (TarArchive.IsTarFile(testStream)) + { + rewindableStream.Rewind(true); + reader = new TarReader(rewindableStream, options, CompressionType.GZip); + return true; + } + + rewindableStream.Rewind(true); + reader = OpenReader(rewindableStream, options); + return true; + } + + return false; + } + + /// + public IReader OpenReader(Stream stream, ReaderOptions? options) + { + return GZipReader.Open(stream, options); + } + + #endregion + + #region IWriterFactory + + /// + public IWriter Open(Stream stream, WriterOptions writerOptions) + { + if (writerOptions.CompressionType != CompressionType.GZip) + { + throw new InvalidFormatException("GZip archives only support GZip compression type."); + } + return new GZipWriter(stream, new GZipWriterOptions(writerOptions)); + } + + #endregion + } +} diff --git a/src/SharpCompress/Factories/IFactory.cs b/src/SharpCompress/Factories/IFactory.cs new file mode 100644 index 00000000..cd1a78f4 --- /dev/null +++ b/src/SharpCompress/Factories/IFactory.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SharpCompress.Factories +{ + /// + /// Represents the foundation of an archive factory. + /// + /// + /// To get extended functionality, this type can be cast to:
+ ///
+ ///
+ ///
+ ///
+ public interface IFactory + { + /// + /// Gets the archive Type name + /// + string Name { get; } + + /// + /// returns the extensions typically used by this archive type. + /// + /// + IEnumerable GetSupportedExtensions(); + + /// + /// returns the archive type in case it is a well known archive. + /// + Common.ArchiveType? KnownArchiveType { get; } + + /// + /// Returns true if the stream represents an archive of the format defined by this type. + /// + /// A stream, pointing to the beginning of the archive. + /// optional password + bool IsArchive(Stream stream, string? password = null); + } +} + diff --git a/src/SharpCompress/Archives/Rar/RarArchiveFactory.cs b/src/SharpCompress/Factories/RarFactory.cs similarity index 56% rename from src/SharpCompress/Archives/Rar/RarArchiveFactory.cs rename to src/SharpCompress/Factories/RarFactory.cs index a049275c..45dd3c54 100644 --- a/src/SharpCompress/Archives/Rar/RarArchiveFactory.cs +++ b/src/SharpCompress/Factories/RarFactory.cs @@ -2,29 +2,42 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; - +using SharpCompress.Archives; +using SharpCompress.Archives.Rar; +using SharpCompress.Common; +using SharpCompress.IO; using SharpCompress.Readers; +using SharpCompress.Readers.Rar; -namespace SharpCompress.Archives.Rar +namespace SharpCompress.Factories { - public class RarArchiveFactory : IArchiveFactory + public class RarFactory : Factory, IArchiveFactory, IReaderFactory { - /// - public string Name => "Rar"; + #region IArchive /// - public IEnumerable GetSupportedExtensions() + public override string Name => "Rar"; + + /// + public override ArchiveType? KnownArchiveType => ArchiveType.Rar; + + /// + public override IEnumerable GetSupportedExtensions() { yield return "rar"; yield return "cbr"; } /// - public bool IsArchive(Stream stream, string? password = null) + public override bool IsArchive(Stream stream, string? password = null) { return RarArchive.IsRarFile(stream); } + #endregion + + #region IArchiveFactory + /// public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) { @@ -48,5 +61,17 @@ namespace SharpCompress.Archives.Rar { return RarArchive.Open(fileInfos, readerOptions); } + + #endregion + + #region IReaderFactory + + /// + public IReader OpenReader(Stream stream, ReaderOptions? options) + { + return RarReader.Open(stream, options); + } + + #endregion } } diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchiveFactory.cs b/src/SharpCompress/Factories/SevenZipFactory.cs similarity index 56% rename from src/SharpCompress/Archives/SevenZip/SevenZipArchiveFactory.cs rename to src/SharpCompress/Factories/SevenZipFactory.cs index 153fb431..df01cf90 100644 --- a/src/SharpCompress/Archives/SevenZip/SevenZipArchiveFactory.cs +++ b/src/SharpCompress/Factories/SevenZipFactory.cs @@ -2,28 +2,40 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; - +using SharpCompress.Archives; +using SharpCompress.Archives.SevenZip; +using SharpCompress.Common; +using SharpCompress.IO; using SharpCompress.Readers; -namespace SharpCompress.Archives.SevenZip +namespace SharpCompress.Factories { - public class SevenZipArchiveFactory : IArchiveFactory + public class SevenZipFactory : Factory, IArchiveFactory { - /// - public string Name => "7Zip"; + #region IFactory /// - public IEnumerable GetSupportedExtensions() + public override string Name => "7Zip"; + + /// + public override ArchiveType? KnownArchiveType => ArchiveType.SevenZip; + + /// + public override IEnumerable GetSupportedExtensions() { yield return "7z"; } /// - public bool IsArchive(Stream stream, string? password = null) + public override bool IsArchive(Stream stream, string? password = null) { return SevenZipArchive.IsSevenZipFile(stream); } + #endregion + + #region IArchiveFactory + /// public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) { @@ -46,6 +58,18 @@ namespace SharpCompress.Archives.SevenZip public IArchive Open(IEnumerable fileInfos, ReaderOptions? readerOptions = null) { return SevenZipArchive.Open(fileInfos, readerOptions); + } + + #endregion + + #region reader + + internal override bool TryOpenReader(RewindableStream rewindableStream, ReaderOptions options, out IReader? reader) + { + reader = null; + return false; } + + #endregion } } diff --git a/src/SharpCompress/Factories/TarFactory.cs b/src/SharpCompress/Factories/TarFactory.cs new file mode 100644 index 00000000..f7c2beba --- /dev/null +++ b/src/SharpCompress/Factories/TarFactory.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using SharpCompress.Archives; +using SharpCompress.Archives.Tar; +using SharpCompress.Common; +using SharpCompress.Compressors; +using SharpCompress.Compressors.BZip2; +using SharpCompress.Compressors.LZMA; +using SharpCompress.Compressors.Xz; +using SharpCompress.IO; +using SharpCompress.Readers; +using SharpCompress.Readers.Tar; +using SharpCompress.Writers; +using SharpCompress.Writers.Tar; + +namespace SharpCompress.Factories +{ + public class TarFactory : Factory, IArchiveFactory, IReaderFactory, IWriterFactory + { + #region IFactory + + /// + public override string Name => "Tar"; + + /// + public override ArchiveType? KnownArchiveType => ArchiveType.Tar; + + /// + public override IEnumerable GetSupportedExtensions() + { + yield return "tar"; + yield return "tgz"; + } + + /// + public override bool IsArchive(Stream stream, string? password = null) + { + return TarArchive.IsTarFile(stream); + } + + #endregion + + #region IArchiveFactory + + /// + public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) + { + return TarArchive.Open(stream, readerOptions); + } + + /// + public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) + { + return TarArchive.Open(fileInfo, readerOptions); + } + + /// + public IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) + { + return TarArchive.Open(streams, readerOptions); + } + + /// + public IArchive Open(IEnumerable fileInfos, ReaderOptions? readerOptions = null) + { + return TarArchive.Open(fileInfos, readerOptions); + } + + #endregion + + #region IReaderFactory + + /// + internal override bool TryOpenReader(RewindableStream rewindableStream, ReaderOptions options, out IReader? reader) + { + reader = null; + + rewindableStream.Rewind(false); + if (TarArchive.IsTarFile(rewindableStream)) + { + rewindableStream.Rewind(true); + reader = OpenReader(rewindableStream, options); + return true; + } + + rewindableStream.Rewind(false); + if (BZip2Stream.IsBZip2(rewindableStream)) + { + rewindableStream.Rewind(false); + var testStream = new BZip2Stream(NonDisposingStream.Create(rewindableStream), CompressionMode.Decompress, false); + if (TarArchive.IsTarFile(testStream)) + { + rewindableStream.Rewind(true); + reader = new TarReader(rewindableStream, options, CompressionType.BZip2); + return true; + } + } + + rewindableStream.Rewind(false); + if (LZipStream.IsLZipFile(rewindableStream)) + { + rewindableStream.Rewind(false); + var testStream = new LZipStream(NonDisposingStream.Create(rewindableStream), CompressionMode.Decompress); + if (TarArchive.IsTarFile(testStream)) + { + rewindableStream.Rewind(true); + reader = new TarReader(rewindableStream, options, CompressionType.LZip); + return true; + } + } + + rewindableStream.Rewind(false); + if (XZStream.IsXZStream(rewindableStream)) + { + rewindableStream.Rewind(true); + var testStream = new XZStream(rewindableStream); + if (TarArchive.IsTarFile(testStream)) + { + rewindableStream.Rewind(true); + reader = new TarReader(rewindableStream, options, CompressionType.Xz); + return true; + } + } + + return false; + } + + /// + public IReader OpenReader(Stream stream, ReaderOptions? options) + { + return TarReader.Open(stream, options); + } + + #endregion + + #region IWriterFactory + + /// + public IWriter Open(Stream stream, WriterOptions writerOptions) + { + return new TarWriter(stream, new TarWriterOptions(writerOptions)); + } + + #endregion + + } +} diff --git a/src/SharpCompress/Factories/ZipFactory.cs b/src/SharpCompress/Factories/ZipFactory.cs new file mode 100644 index 00000000..dcbb2b25 --- /dev/null +++ b/src/SharpCompress/Factories/ZipFactory.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using SharpCompress.Archives; +using SharpCompress.Archives.Zip; +using SharpCompress.Common; +using SharpCompress.Common.Tar.Headers; +using SharpCompress.IO; +using SharpCompress.Readers; +using SharpCompress.Readers.Zip; +using SharpCompress.Writers; +using SharpCompress.Writers.Zip; + +namespace SharpCompress.Factories +{ + public class ZipFactory : Factory, IArchiveFactory, IReaderFactory, IWriterFactory + { + #region IFactory + + /// + public override string Name => "Zip"; + + /// + public override ArchiveType? KnownArchiveType => ArchiveType.Zip; + + /// + public override IEnumerable GetSupportedExtensions() + { + yield return "zip"; + yield return "zipx"; + yield return "cbz"; + } + + /// + public override bool IsArchive(Stream stream, string? password = null) + { + var startPosition = stream.CanSeek + ? stream.Position + : -1; + + // probe for single volume zip + + if (ZipArchive.IsZipFile(stream, password)) + return true; + + // probe for a multipart zip + + if (!stream.CanSeek) + return false; + + stream.Position = startPosition; + + //test the zip (last) file of a multipart zip + if (ZipArchive.IsZipMulti(stream)) + return true; + + return false; + } + + #endregion + + #region IArchiveFactory + + /// + public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) + { + return ZipArchive.Open(stream, readerOptions); + } + + /// + public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) + { + return ZipArchive.Open(fileInfo, readerOptions); + } + + /// + public IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) + { + return ZipArchive.Open(streams, readerOptions); + } + + /// + public IArchive Open(IEnumerable fileInfos, ReaderOptions? readerOptions = null) + { + return ZipArchive.Open(fileInfos, readerOptions); + } + + #endregion + + #region IReaderFactory + + /// + public IReader OpenReader(Stream stream, ReaderOptions? options) + { + return ZipReader.Open(stream, options); + } + + #endregion + + #region IWriterFactory + + /// + public IWriter Open(Stream stream, WriterOptions writerOptions) + { + return new ZipWriter(stream, new ZipWriterOptions(writerOptions)); + } + + #endregion + } +} diff --git a/src/SharpCompress/Readers/IReaderFactory.cs b/src/SharpCompress/Readers/IReaderFactory.cs new file mode 100644 index 00000000..1f6f7408 --- /dev/null +++ b/src/SharpCompress/Readers/IReaderFactory.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using SharpCompress.IO; + +namespace SharpCompress.Readers +{ + public interface IReaderFactory : Factories.IFactory + { + /// + /// Opens a Reader for Non-seeking usage + /// + /// + /// + /// + IReader OpenReader(Stream stream, ReaderOptions? options); + } +} + diff --git a/src/SharpCompress/Readers/ReaderFactory.cs b/src/SharpCompress/Readers/ReaderFactory.cs index adff51cc..5f75ed0d 100644 --- a/src/SharpCompress/Readers/ReaderFactory.cs +++ b/src/SharpCompress/Readers/ReaderFactory.cs @@ -1,20 +1,9 @@ using System; using System.IO; -using SharpCompress.Archives.GZip; -using SharpCompress.Archives.Rar; -using SharpCompress.Archives.Tar; -using SharpCompress.Archives.Zip; +using System.Linq; + using SharpCompress.Common; -using SharpCompress.Compressors; -using SharpCompress.Compressors.BZip2; -using SharpCompress.Compressors.Deflate; using SharpCompress.IO; -using SharpCompress.Readers.GZip; -using SharpCompress.Readers.Rar; -using SharpCompress.Readers.Tar; -using SharpCompress.Readers.Zip; -using SharpCompress.Compressors.LZMA; -using SharpCompress.Compressors.Xz; namespace SharpCompress.Readers { @@ -33,74 +22,15 @@ namespace SharpCompress.Readers { LeaveStreamOpen = false }; + RewindableStream rewindableStream = new RewindableStream(stream); rewindableStream.StartRecording(); - if (ZipArchive.IsZipFile(rewindableStream, options.Password)) - { - rewindableStream.Rewind(true); - return ZipReader.Open(rewindableStream, options); - } - rewindableStream.Rewind(false); - if (GZipArchive.IsGZipFile(rewindableStream)) - { - rewindableStream.Rewind(false); - GZipStream testStream = new GZipStream(rewindableStream, CompressionMode.Decompress); - if (TarArchive.IsTarFile(testStream)) - { - rewindableStream.Rewind(true); - return new TarReader(rewindableStream, options, CompressionType.GZip); - } - rewindableStream.Rewind(true); - return GZipReader.Open(rewindableStream, options); - } - rewindableStream.Rewind(false); - if (BZip2Stream.IsBZip2(rewindableStream)) + foreach(var factory in Factories.Factory.Factories.OfType()) { - rewindableStream.Rewind(false); - BZip2Stream testStream = new BZip2Stream(NonDisposingStream.Create(rewindableStream), CompressionMode.Decompress, false); - if (TarArchive.IsTarFile(testStream)) - { - rewindableStream.Rewind(true); - return new TarReader(rewindableStream, options, CompressionType.BZip2); - } - } + if (factory.TryOpenReader(rewindableStream, options, out var reader) && reader != null) return reader; + } - rewindableStream.Rewind(false); - if (LZipStream.IsLZipFile(rewindableStream)) - { - rewindableStream.Rewind(false); - LZipStream testStream = new LZipStream(NonDisposingStream.Create(rewindableStream), CompressionMode.Decompress); - if (TarArchive.IsTarFile(testStream)) - { - rewindableStream.Rewind(true); - return new TarReader(rewindableStream, options, CompressionType.LZip); - } - } - rewindableStream.Rewind(false); - if (RarArchive.IsRarFile(rewindableStream, options)) - { - rewindableStream.Rewind(true); - return RarReader.Open(rewindableStream, options); - } - - rewindableStream.Rewind(false); - if (TarArchive.IsTarFile(rewindableStream)) - { - rewindableStream.Rewind(true); - return TarReader.Open(rewindableStream, options); - } - rewindableStream.Rewind(false); - if (XZStream.IsXZStream(rewindableStream)) - { - rewindableStream.Rewind(true); - XZStream testStream = new XZStream(rewindableStream); - if (TarArchive.IsTarFile(testStream)) - { - rewindableStream.Rewind(true); - return new TarReader(rewindableStream, options, CompressionType.Xz); - } - } throw new InvalidOperationException("Cannot determine compressed stream type. Supported Reader Formats: Zip, GZip, BZip2, Tar, Rar, LZip, XZ"); } } diff --git a/src/SharpCompress/Writers/IWriterFactory.cs b/src/SharpCompress/Writers/IWriterFactory.cs new file mode 100644 index 00000000..1aed7c72 --- /dev/null +++ b/src/SharpCompress/Writers/IWriterFactory.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using SharpCompress.Factories; + +namespace SharpCompress.Writers +{ + public interface IWriterFactory : IFactory + { + IWriter Open(Stream stream, WriterOptions writerOptions); + } +} diff --git a/src/SharpCompress/Writers/WriterFactory.cs b/src/SharpCompress/Writers/WriterFactory.cs index fcb0fd41..60aa613c 100644 --- a/src/SharpCompress/Writers/WriterFactory.cs +++ b/src/SharpCompress/Writers/WriterFactory.cs @@ -1,9 +1,8 @@ -using System; +using System; using System.IO; +using System.Linq; + using SharpCompress.Common; -using SharpCompress.Writers.GZip; -using SharpCompress.Writers.Tar; -using SharpCompress.Writers.Zip; namespace SharpCompress.Writers { @@ -11,29 +10,15 @@ namespace SharpCompress.Writers { public static IWriter Open(Stream stream, ArchiveType archiveType, WriterOptions writerOptions) { - switch (archiveType) - { - case ArchiveType.GZip: - { - if (writerOptions.CompressionType != CompressionType.GZip) - { - throw new InvalidFormatException("GZip archives only support GZip compression type."); - } - return new GZipWriter(stream, new GZipWriterOptions(writerOptions)); - } - case ArchiveType.Zip: - { - return new ZipWriter(stream, new ZipWriterOptions(writerOptions)); - } - case ArchiveType.Tar: - { - return new TarWriter(stream, new TarWriterOptions(writerOptions)); - } - default: - { - throw new NotSupportedException("Archive Type does not have a Writer: " + archiveType); - } - } + var factory = Factories.Factory + .Factories + .OfType() + .FirstOrDefault(item => item.KnownArchiveType == archiveType); + + if (factory != null) + return factory.Open(stream, writerOptions); + + throw new NotSupportedException("Archive Type does not have a Writer: " + archiveType); } } -} \ No newline at end of file +} From ca3d0887854bae112aba74d1baa45a4c2e6c0203 Mon Sep 17 00:00:00 2001 From: Vicente Penades <5433822+vpenades@users.noreply.github.com> Date: Sat, 3 Dec 2022 13:44:19 +0100 Subject: [PATCH 2/6] generalized multipart archive interface --- src/SharpCompress/Archives/ArchiveFactory.cs | 52 +++++++++---------- src/SharpCompress/Archives/IArchiveFactory.cs | 16 ------ .../Archives/IMultiArchiveFactory.cs | 40 ++++++++++++++ src/SharpCompress/Factories/Factory.cs | 6 +++ src/SharpCompress/Factories/GZipFactory.cs | 6 ++- src/SharpCompress/Factories/IFactory.cs | 10 ++++ src/SharpCompress/Factories/RarFactory.cs | 14 ++++- .../Factories/SevenZipFactory.cs | 9 +++- src/SharpCompress/Factories/TarFactory.cs | 8 ++- src/SharpCompress/Factories/ZipFactory.cs | 12 ++++- 10 files changed, 122 insertions(+), 51 deletions(-) create mode 100644 src/SharpCompress/Archives/IMultiArchiveFactory.cs diff --git a/src/SharpCompress/Archives/ArchiveFactory.cs b/src/SharpCompress/Archives/ArchiveFactory.cs index 9de21b07..c4185d18 100644 --- a/src/SharpCompress/Archives/ArchiveFactory.cs +++ b/src/SharpCompress/Archives/ArchiveFactory.cs @@ -8,6 +8,7 @@ using SharpCompress.Archives.SevenZip; using SharpCompress.Archives.Tar; using SharpCompress.Archives.Zip; using SharpCompress.Common; +using SharpCompress.Factories; using SharpCompress.IO; using SharpCompress.Readers; @@ -23,15 +24,9 @@ namespace SharpCompress.Archives /// public static IArchive Open(Stream stream, ReaderOptions? readerOptions = null) { - stream.CheckNotNull(nameof(stream)); - if (!stream.CanRead || !stream.CanSeek) - { - throw new ArgumentException("Stream should be readable and seekable"); - } - readerOptions ??= new ReaderOptions(); - return FindArchiveFactory(stream).Open(stream, readerOptions); + return FindFactory(stream).Open(stream, readerOptions); } public static IWritableArchive Create(ArchiveType type) @@ -62,11 +57,10 @@ namespace SharpCompress.Archives /// /// public static IArchive Open(FileInfo fileInfo, ReaderOptions? options = null) - { - fileInfo.CheckNotNull(nameof(fileInfo)); + { options ??= new ReaderOptions { LeaveStreamOpen = false }; - return FindArchiveFactory(fileInfo).Open(fileInfo, options); + return FindFactory(fileInfo).Open(fileInfo, options); } /// @@ -87,7 +81,7 @@ namespace SharpCompress.Archives fileInfo.CheckNotNull(nameof(fileInfo)); options ??= new ReaderOptions { LeaveStreamOpen = false }; - return FindArchiveFactory(fileInfo).Open(fileInfos, options); + return FindFactory(fileInfo).Open(fileInfos, options); } /// @@ -108,7 +102,7 @@ namespace SharpCompress.Archives firstStream.CheckNotNull(nameof(firstStream)); options ??= new ReaderOptions(); - return FindArchiveFactory(firstStream).Open(streamsArray, options); + return FindFactory(firstStream).Open(streamsArray, options); } /// @@ -124,16 +118,18 @@ namespace SharpCompress.Archives } } - private static IArchiveFactory FindArchiveFactory(FileInfo finfo) + private static T FindFactory(FileInfo finfo) + where T : IFactory { finfo.CheckNotNull(nameof(finfo)); using (Stream stream = finfo.OpenRead()) { - return FindArchiveFactory(stream); + return FindFactory(stream); } } - private static IArchiveFactory FindArchiveFactory(Stream stream) + private static T FindFactory(Stream stream) + where T: IFactory { stream.CheckNotNull(nameof(stream)); if (!stream.CanRead || !stream.CanSeek) @@ -141,7 +137,7 @@ namespace SharpCompress.Archives throw new ArgumentException("Stream should be readable and seekable"); } - var factores = Factories.Factory.Factories.OfType(); + var factores = Factories.Factory.Factories.OfType(); long startPosition = stream.Position; @@ -214,21 +210,21 @@ namespace SharpCompress.Archives public static IEnumerable GetFileParts(FileInfo part1) { part1.CheckNotNull(nameof(part1)); - yield return part1; - int i = 1; + yield return part1; - FileInfo? part = RarArchiveVolumeFactory.GetFilePart(i++, part1); - if (part != null) + foreach(var factory in Factory.Factories.OfType()) { - yield return part; - while ((part = RarArchiveVolumeFactory.GetFilePart(i++, part1)) != null) //tests split too - yield return part; - } - else - { - i = 1; - while ((part = ZipArchiveVolumeFactory.GetFilePart(i++, part1)) != null) //tests split too + int i = 1; + FileInfo? part = factory.GetFilePart(i++, part1); + + if (part != null) + { yield return part; + while ((part = factory.GetFilePart(i++, part1)) != null) //tests split too + yield return part; + + yield break; + } } } } diff --git a/src/SharpCompress/Archives/IArchiveFactory.cs b/src/SharpCompress/Archives/IArchiveFactory.cs index 0bf43638..98014688 100644 --- a/src/SharpCompress/Archives/IArchiveFactory.cs +++ b/src/SharpCompress/Archives/IArchiveFactory.cs @@ -23,8 +23,6 @@ namespace SharpCompress.Archives /// public interface IArchiveFactory : IFactory { - - /// /// Opens an Archive for random access. /// @@ -38,19 +36,5 @@ namespace SharpCompress.Archives /// the file to open. /// reading options. IArchive Open(System.IO.FileInfo fileInfo, ReaderOptions? readerOptions = null); - - /// - /// Constructor with IEnumerable FileInfo objects, multi and split support. - /// - /// - /// reading options. - IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null); - - /// - /// Constructor with IEnumerable Stream objects, multi and split support. - /// - /// - /// reading options. - IArchive Open(IEnumerable fileInfos, ReaderOptions? readerOptions = null); } } diff --git a/src/SharpCompress/Archives/IMultiArchiveFactory.cs b/src/SharpCompress/Archives/IMultiArchiveFactory.cs new file mode 100644 index 00000000..05c664a1 --- /dev/null +++ b/src/SharpCompress/Archives/IMultiArchiveFactory.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.IO; + +using SharpCompress.Common; +using SharpCompress.Factories; +using SharpCompress.Readers; + +namespace SharpCompress.Archives +{ + /// + /// Represents a factory used to identify and open archives. + /// + /// + /// Currently implemented by:
+ /// + /// + /// + /// + /// + /// + /// + ///
+ public interface IMultiArchiveFactory : IFactory + { + /// + /// Constructor with IEnumerable FileInfo objects, multi and split support. + /// + /// + /// reading options. + IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null); + + /// + /// Constructor with IEnumerable Stream objects, multi and split support. + /// + /// + /// reading options. + IArchive Open(IEnumerable fileInfos, ReaderOptions? readerOptions = null); + } +} diff --git a/src/SharpCompress/Factories/Factory.cs b/src/SharpCompress/Factories/Factory.cs index dd4ede80..9f5cb001 100644 --- a/src/SharpCompress/Factories/Factory.cs +++ b/src/SharpCompress/Factories/Factory.cs @@ -55,6 +55,12 @@ namespace SharpCompress.Factories /// public abstract bool IsArchive(Stream stream, string? password = null); + /// + public virtual FileInfo? GetFilePart(int index, FileInfo part1) + { + return null; + } + /// /// Tries to open an from a . /// diff --git a/src/SharpCompress/Factories/GZipFactory.cs b/src/SharpCompress/Factories/GZipFactory.cs index 6e9fb6a8..2011f12a 100644 --- a/src/SharpCompress/Factories/GZipFactory.cs +++ b/src/SharpCompress/Factories/GZipFactory.cs @@ -17,7 +17,7 @@ using SharpCompress.Writers.GZip; namespace SharpCompress.Factories { - public class GZipFactory : Factory, IArchiveFactory, IReaderFactory, IWriterFactory + public class GZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory, IReaderFactory, IWriterFactory { #region IFactory @@ -55,6 +55,10 @@ namespace SharpCompress.Factories return GZipArchive.Open(fileInfo, readerOptions); } + #endregion + + #region IMultiArchiveFactory + /// public IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) { diff --git a/src/SharpCompress/Factories/IFactory.cs b/src/SharpCompress/Factories/IFactory.cs index cd1a78f4..778ea4a6 100644 --- a/src/SharpCompress/Factories/IFactory.cs +++ b/src/SharpCompress/Factories/IFactory.cs @@ -40,6 +40,16 @@ namespace SharpCompress.Factories /// A stream, pointing to the beginning of the archive. /// optional password bool IsArchive(Stream stream, string? password = null); + + /// + /// From a passed in archive (zip, rar, 7z, 001), return all parts. + /// + /// Path to the first part. + /// + /// The path to the requested part, + /// or NULL if the part does not exist. + /// + FileInfo? GetFilePart(int index, FileInfo part1); } } diff --git a/src/SharpCompress/Factories/RarFactory.cs b/src/SharpCompress/Factories/RarFactory.cs index 45dd3c54..1bbc8eaa 100644 --- a/src/SharpCompress/Factories/RarFactory.cs +++ b/src/SharpCompress/Factories/RarFactory.cs @@ -11,7 +11,7 @@ using SharpCompress.Readers.Rar; namespace SharpCompress.Factories { - public class RarFactory : Factory, IArchiveFactory, IReaderFactory + public class RarFactory : Factory, IArchiveFactory, IMultiArchiveFactory, IReaderFactory { #region IArchive @@ -34,9 +34,15 @@ namespace SharpCompress.Factories return RarArchive.IsRarFile(stream); } + /// + public override FileInfo? GetFilePart(int index, FileInfo part1) + { + return RarArchiveVolumeFactory.GetFilePart(index, part1); + } + #endregion - #region IArchiveFactory + #region IArchiveFactory /// public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) @@ -50,6 +56,10 @@ namespace SharpCompress.Factories return RarArchive.Open(fileInfo, readerOptions); } + #endregion + + #region IMultiArchiveFactory + /// public IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) { diff --git a/src/SharpCompress/Factories/SevenZipFactory.cs b/src/SharpCompress/Factories/SevenZipFactory.cs index df01cf90..9ca55885 100644 --- a/src/SharpCompress/Factories/SevenZipFactory.cs +++ b/src/SharpCompress/Factories/SevenZipFactory.cs @@ -5,12 +5,13 @@ using System.Linq; using SharpCompress.Archives; using SharpCompress.Archives.SevenZip; using SharpCompress.Common; +using SharpCompress.Common.SevenZip; using SharpCompress.IO; using SharpCompress.Readers; namespace SharpCompress.Factories { - public class SevenZipFactory : Factory, IArchiveFactory + public class SevenZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory { #region IFactory @@ -34,7 +35,7 @@ namespace SharpCompress.Factories #endregion - #region IArchiveFactory + #region IArchiveFactory /// public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) @@ -48,6 +49,10 @@ namespace SharpCompress.Factories return SevenZipArchive.Open(fileInfo, readerOptions); } + #endregion + + #region IMultiArchiveFactory + /// public IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) { diff --git a/src/SharpCompress/Factories/TarFactory.cs b/src/SharpCompress/Factories/TarFactory.cs index f7c2beba..d4a7c2fe 100644 --- a/src/SharpCompress/Factories/TarFactory.cs +++ b/src/SharpCompress/Factories/TarFactory.cs @@ -2,8 +2,10 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; + using SharpCompress.Archives; using SharpCompress.Archives.Tar; +using SharpCompress.Archives.Zip; using SharpCompress.Common; using SharpCompress.Compressors; using SharpCompress.Compressors.BZip2; @@ -17,7 +19,7 @@ using SharpCompress.Writers.Tar; namespace SharpCompress.Factories { - public class TarFactory : Factory, IArchiveFactory, IReaderFactory, IWriterFactory + public class TarFactory : Factory, IArchiveFactory, IMultiArchiveFactory, IReaderFactory, IWriterFactory { #region IFactory @@ -56,6 +58,10 @@ namespace SharpCompress.Factories return TarArchive.Open(fileInfo, readerOptions); } + #endregion + + #region IMultiArchiveFactory + /// public IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) { diff --git a/src/SharpCompress/Factories/ZipFactory.cs b/src/SharpCompress/Factories/ZipFactory.cs index dcbb2b25..3ff73819 100644 --- a/src/SharpCompress/Factories/ZipFactory.cs +++ b/src/SharpCompress/Factories/ZipFactory.cs @@ -15,7 +15,7 @@ using SharpCompress.Writers.Zip; namespace SharpCompress.Factories { - public class ZipFactory : Factory, IArchiveFactory, IReaderFactory, IWriterFactory + public class ZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory, IReaderFactory, IWriterFactory { #region IFactory @@ -59,6 +59,12 @@ namespace SharpCompress.Factories return false; } + /// + public override FileInfo? GetFilePart(int index, FileInfo part1) + { + return ZipArchiveVolumeFactory.GetFilePart(index, part1); + } + #endregion #region IArchiveFactory @@ -75,6 +81,10 @@ namespace SharpCompress.Factories return ZipArchive.Open(fileInfo, readerOptions); } + #endregion + + #region IMultiArchiveFactory + /// public IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) { From a22393075f372422caf4d3356a477c74948f63f6 Mon Sep 17 00:00:00 2001 From: vpenades <5433822+vpenades@users.noreply.github.com> Date: Mon, 5 Dec 2022 09:31:30 +0100 Subject: [PATCH 3/6] Generalized archive creation --- src/SharpCompress/Archives/ArchiveFactory.cs | 33 ++++++++++--------- .../Archives/IMultiArchiveFactory.cs | 6 ++-- .../Archives/IWriteableArchiveFactory.cs | 27 +++++++++++++++ src/SharpCompress/Factories/GZipFactory.cs | 24 ++++++++++++-- src/SharpCompress/Factories/RarFactory.cs | 12 +++++-- .../Factories/SevenZipFactory.cs | 11 +++++-- src/SharpCompress/Factories/TarFactory.cs | 24 ++++++++++++-- src/SharpCompress/Factories/ZipFactory.cs | 24 ++++++++++++-- 8 files changed, 128 insertions(+), 33 deletions(-) create mode 100644 src/SharpCompress/Archives/IWriteableArchiveFactory.cs diff --git a/src/SharpCompress/Archives/ArchiveFactory.cs b/src/SharpCompress/Archives/ArchiveFactory.cs index c4185d18..71c0358e 100644 --- a/src/SharpCompress/Archives/ArchiveFactory.cs +++ b/src/SharpCompress/Archives/ArchiveFactory.cs @@ -31,13 +31,16 @@ namespace SharpCompress.Archives public static IWritableArchive Create(ArchiveType type) { - return type switch - { - ArchiveType.Zip => ZipArchive.Create(), - ArchiveType.Tar => TarArchive.Create(), - ArchiveType.GZip => GZipArchive.Create(), - _ => throw new NotSupportedException("Cannot create Archives of type: " + type) - }; + var factory = Factories.Factory + .Factories + .OfType() + .Where(item => item.KnownArchiveType == type) + .FirstOrDefault(); + + if (factory != null) + return factory.CreateWriteableArchive(); + + throw new NotSupportedException("Cannot create Archives of type: " + type); } /// @@ -71,17 +74,17 @@ namespace SharpCompress.Archives public static IArchive Open(IEnumerable fileInfos, ReaderOptions? options = null) { fileInfos.CheckNotNull(nameof(fileInfos)); - FileInfo[] files = fileInfos.ToArray(); - if (files.Length == 0) + FileInfo[] filesArray = fileInfos.ToArray(); + if (filesArray.Length == 0) throw new InvalidOperationException("No files to open"); - FileInfo fileInfo = files[0]; - if (files.Length == 1) + FileInfo fileInfo = filesArray[0]; + if (filesArray.Length == 1) return Open(fileInfo, options); fileInfo.CheckNotNull(nameof(fileInfo)); options ??= new ReaderOptions { LeaveStreamOpen = false }; - return FindFactory(fileInfo).Open(fileInfos, options); + return FindFactory(fileInfo).Open(filesArray, options); } /// @@ -137,11 +140,11 @@ namespace SharpCompress.Archives throw new ArgumentException("Stream should be readable and seekable"); } - var factores = Factories.Factory.Factories.OfType(); + var factories = Factories.Factory.Factories.OfType(); long startPosition = stream.Position; - foreach (var factory in factores) + foreach (var factory in factories) { stream.Seek(startPosition, SeekOrigin.Begin); @@ -153,7 +156,7 @@ namespace SharpCompress.Archives } } - var extensions = string.Join(", ", factores.Select(item => item.Name)); + var extensions = string.Join(", ", factories.Select(item => item.Name)); throw new InvalidOperationException($"Cannot determine compressed stream type. Supported Archive Formats: {extensions}"); } diff --git a/src/SharpCompress/Archives/IMultiArchiveFactory.cs b/src/SharpCompress/Archives/IMultiArchiveFactory.cs index 05c664a1..6ba39280 100644 --- a/src/SharpCompress/Archives/IMultiArchiveFactory.cs +++ b/src/SharpCompress/Archives/IMultiArchiveFactory.cs @@ -12,7 +12,7 @@ namespace SharpCompress.Archives /// Represents a factory used to identify and open archives. /// /// - /// Currently implemented by:
+ /// Implemented by:
/// /// /// @@ -28,13 +28,13 @@ namespace SharpCompress.Archives ///
/// /// reading options. - IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null); + IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null); /// /// Constructor with IEnumerable Stream objects, multi and split support. /// /// /// reading options. - IArchive Open(IEnumerable fileInfos, ReaderOptions? readerOptions = null); + IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null); } } diff --git a/src/SharpCompress/Archives/IWriteableArchiveFactory.cs b/src/SharpCompress/Archives/IWriteableArchiveFactory.cs new file mode 100644 index 00000000..59c58134 --- /dev/null +++ b/src/SharpCompress/Archives/IWriteableArchiveFactory.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SharpCompress.Archives +{ + /// + /// Decorator for used to declare an archive format as able to create writeable archives + /// + /// + /// Implemented by:
+ /// + /// + /// + /// + /// + public interface IWriteableArchiveFactory : Factories.IFactory + { + /// + /// Creates a new, empty archive, ready to be written. + /// + /// + IWritableArchive CreateWriteableArchive(); + } +} diff --git a/src/SharpCompress/Factories/GZipFactory.cs b/src/SharpCompress/Factories/GZipFactory.cs index 2011f12a..fe6f4260 100644 --- a/src/SharpCompress/Factories/GZipFactory.cs +++ b/src/SharpCompress/Factories/GZipFactory.cs @@ -17,7 +17,15 @@ using SharpCompress.Writers.GZip; namespace SharpCompress.Factories { - public class GZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory, IReaderFactory, IWriterFactory + /// + /// Represents the foundation factory of GZip archive. + /// + public class GZipFactory : Factory, + IArchiveFactory, + IMultiArchiveFactory, + IReaderFactory, + IWriterFactory, + IWriteableArchiveFactory { #region IFactory @@ -60,13 +68,13 @@ namespace SharpCompress.Factories #region IMultiArchiveFactory /// - public IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) + public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) { return GZipArchive.Open(streams, readerOptions); } /// - public IArchive Open(IEnumerable fileInfos, ReaderOptions? readerOptions = null) + public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) { return GZipArchive.Open(fileInfos, readerOptions); } @@ -121,5 +129,15 @@ namespace SharpCompress.Factories } #endregion + + #region IWriteableArchiveFactory + + /// + public IWritableArchive CreateWriteableArchive() + { + return GZipArchive.Create(); + } + + #endregion } } diff --git a/src/SharpCompress/Factories/RarFactory.cs b/src/SharpCompress/Factories/RarFactory.cs index 1bbc8eaa..7d6c16ba 100644 --- a/src/SharpCompress/Factories/RarFactory.cs +++ b/src/SharpCompress/Factories/RarFactory.cs @@ -11,7 +11,13 @@ using SharpCompress.Readers.Rar; namespace SharpCompress.Factories { - public class RarFactory : Factory, IArchiveFactory, IMultiArchiveFactory, IReaderFactory + /// + /// Represents the foundation factory of RAR archive. + /// + public class RarFactory : Factory, + IArchiveFactory, + IMultiArchiveFactory, + IReaderFactory { #region IArchive @@ -61,13 +67,13 @@ namespace SharpCompress.Factories #region IMultiArchiveFactory /// - public IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) + public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) { return RarArchive.Open(streams, readerOptions); } /// - public IArchive Open(IEnumerable fileInfos, ReaderOptions? readerOptions = null) + public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) { return RarArchive.Open(fileInfos, readerOptions); } diff --git a/src/SharpCompress/Factories/SevenZipFactory.cs b/src/SharpCompress/Factories/SevenZipFactory.cs index 9ca55885..c7d4dd8b 100644 --- a/src/SharpCompress/Factories/SevenZipFactory.cs +++ b/src/SharpCompress/Factories/SevenZipFactory.cs @@ -11,7 +11,12 @@ using SharpCompress.Readers; namespace SharpCompress.Factories { - public class SevenZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory + /// + /// Represents the foundation factory of 7Zip archive. + /// + public class SevenZipFactory : Factory, + IArchiveFactory, + IMultiArchiveFactory { #region IFactory @@ -54,13 +59,13 @@ namespace SharpCompress.Factories #region IMultiArchiveFactory /// - public IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) + public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) { return SevenZipArchive.Open(streams, readerOptions); } /// - public IArchive Open(IEnumerable fileInfos, ReaderOptions? readerOptions = null) + public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) { return SevenZipArchive.Open(fileInfos, readerOptions); } diff --git a/src/SharpCompress/Factories/TarFactory.cs b/src/SharpCompress/Factories/TarFactory.cs index d4a7c2fe..c56c383b 100644 --- a/src/SharpCompress/Factories/TarFactory.cs +++ b/src/SharpCompress/Factories/TarFactory.cs @@ -19,7 +19,15 @@ using SharpCompress.Writers.Tar; namespace SharpCompress.Factories { - public class TarFactory : Factory, IArchiveFactory, IMultiArchiveFactory, IReaderFactory, IWriterFactory + /// + /// Represents the foundation factory of TAR archive. + /// + public class TarFactory : Factory, + IArchiveFactory, + IMultiArchiveFactory, + IReaderFactory, + IWriterFactory, + IWriteableArchiveFactory { #region IFactory @@ -63,13 +71,13 @@ namespace SharpCompress.Factories #region IMultiArchiveFactory /// - public IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) + public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) { return TarArchive.Open(streams, readerOptions); } /// - public IArchive Open(IEnumerable fileInfos, ReaderOptions? readerOptions = null) + public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) { return TarArchive.Open(fileInfos, readerOptions); } @@ -151,5 +159,15 @@ namespace SharpCompress.Factories #endregion + #region IWriteableArchiveFactory + + /// + public IWritableArchive CreateWriteableArchive() + { + return TarArchive.Create(); + } + + #endregion + } } diff --git a/src/SharpCompress/Factories/ZipFactory.cs b/src/SharpCompress/Factories/ZipFactory.cs index 3ff73819..a3e6e757 100644 --- a/src/SharpCompress/Factories/ZipFactory.cs +++ b/src/SharpCompress/Factories/ZipFactory.cs @@ -15,7 +15,15 @@ using SharpCompress.Writers.Zip; namespace SharpCompress.Factories { - public class ZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory, IReaderFactory, IWriterFactory + /// + /// Represents the foundation factory of ZIP archive. + /// + public class ZipFactory : Factory, + IArchiveFactory, + IMultiArchiveFactory, + IReaderFactory, + IWriterFactory, + IWriteableArchiveFactory { #region IFactory @@ -86,13 +94,13 @@ namespace SharpCompress.Factories #region IMultiArchiveFactory /// - public IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) + public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) { return ZipArchive.Open(streams, readerOptions); } /// - public IArchive Open(IEnumerable fileInfos, ReaderOptions? readerOptions = null) + public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) { return ZipArchive.Open(fileInfos, readerOptions); } @@ -118,5 +126,15 @@ namespace SharpCompress.Factories } #endregion + + #region IWriteableArchiveFactory + + /// + public IWritableArchive CreateWriteableArchive() + { + return ZipArchive.Create(); + } + + #endregion } } From 37c7251ec90028819ede962cc1522db7f55a6300 Mon Sep 17 00:00:00 2001 From: vpenades <5433822+vpenades@users.noreply.github.com> Date: Mon, 5 Dec 2022 22:29:28 +0100 Subject: [PATCH 4/6] added additional TAR extensions --- src/SharpCompress/Factories/TarFactory.cs | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/SharpCompress/Factories/TarFactory.cs b/src/SharpCompress/Factories/TarFactory.cs index c56c383b..9eb4cd04 100644 --- a/src/SharpCompress/Factories/TarFactory.cs +++ b/src/SharpCompress/Factories/TarFactory.cs @@ -40,8 +40,32 @@ namespace SharpCompress.Factories /// public override IEnumerable GetSupportedExtensions() { + // from https://en.wikipedia.org/wiki/Tar_(computing)#Suffixes_for_compressed_files + yield return "tar"; + + // gzip + yield return "taz"; yield return "tgz"; + + // bzip2 + yield return "tb2"; + yield return "tbz"; + yield return "tbz2"; + yield return "tz2"; + + // lzma + yield return "tlz"; + + // xz + yield return "txz"; + + // compress + yield return "tZ"; + yield return "taZ"; + + // zstd + yield return "tzst"; } /// From 17dab3df343b5d96524d3277e8f6b3144d090fa9 Mon Sep 17 00:00:00 2001 From: vpenades <5433822+vpenades@users.noreply.github.com> Date: Mon, 5 Dec 2022 22:36:18 +0100 Subject: [PATCH 5/6] small refactor --- src/SharpCompress/Factories/IFactory.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/SharpCompress/Factories/IFactory.cs b/src/SharpCompress/Factories/IFactory.cs index 778ea4a6..82e27f11 100644 --- a/src/SharpCompress/Factories/IFactory.cs +++ b/src/SharpCompress/Factories/IFactory.cs @@ -23,16 +23,16 @@ namespace SharpCompress.Factories ///
string Name { get; } + /// + /// Gets the archive Type in case it is a well known archive format. + /// + Common.ArchiveType? KnownArchiveType { get; } + /// /// returns the extensions typically used by this archive type. /// /// - IEnumerable GetSupportedExtensions(); - - /// - /// returns the archive type in case it is a well known archive. - /// - Common.ArchiveType? KnownArchiveType { get; } + IEnumerable GetSupportedExtensions(); /// /// Returns true if the stream represents an archive of the format defined by this type. From 891d5d3c35992fe88a5cc945a55032918c15e0b1 Mon Sep 17 00:00:00 2001 From: vpenades <5433822+vpenades@users.noreply.github.com> Date: Tue, 6 Dec 2022 10:13:09 +0100 Subject: [PATCH 6/6] commented unsupported extensions --- src/SharpCompress/Factories/TarFactory.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SharpCompress/Factories/TarFactory.cs b/src/SharpCompress/Factories/TarFactory.cs index 9eb4cd04..4214ca93 100644 --- a/src/SharpCompress/Factories/TarFactory.cs +++ b/src/SharpCompress/Factories/TarFactory.cs @@ -55,17 +55,17 @@ namespace SharpCompress.Factories yield return "tz2"; // lzma - yield return "tlz"; + // yield return "tlz"; // unsupported // xz - yield return "txz"; + // yield return "txz"; // unsupported // compress yield return "tZ"; yield return "taZ"; // zstd - yield return "tzst"; + // yield return "tzst"; // unsupported } ///