Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduced IArchiveFactory #671

Merged
merged 7 commits into from
Sep 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 80 additions & 91 deletions src/SharpCompress/Archives/ArchiveFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,36 @@ namespace SharpCompress.Archives
{
public static class ArchiveFactory
{
private static readonly HashSet<IArchiveFactory> archiveFactories;

/// <summary>
/// Gets the collection of registered archive factories
/// </summary>
public static IReadOnlyCollection<IArchiveFactory> Factories => archiveFactories;

static ArchiveFactory()
{
archiveFactories = new HashSet<IArchiveFactory>();

RegisterFactory(new Zip.ZipArchiveFactory());
RegisterFactory(new Rar.RarArchiveFactory());
RegisterFactory(new SevenZip.SevenZipArchiveFactory());
RegisterFactory(new GZip.GZipArchiveFactory());
RegisterFactory(new Tar.TarArchiveFactory());
}

/// <summary>
/// Registers an archive factory.
/// </summary>
/// <param name="factory">The factory to register.</param>
/// <exception cref="ArgumentNullException"><paramref name="factory"/> must not be null.</exception>
public static void RegisterFactory(IArchiveFactory factory)
{
factory.CheckNotNull(nameof(factory));

archiveFactories.Add(factory);
}

/// <summary>
/// Opens an Archive for random access
/// </summary>
Expand All @@ -28,28 +58,10 @@ public static IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
throw new ArgumentException("Stream should be readable and seekable");
}
readerOptions ??= new ReaderOptions();

ArchiveType? type;
IsArchive(stream, out type); //test and reset stream position
readerOptions ??= new ReaderOptions();

if (type != null)
{
switch (type.Value)
{
case ArchiveType.Zip:
return ZipArchive.Open(stream, readerOptions);
case ArchiveType.SevenZip:
return SevenZipArchive.Open(stream, readerOptions);
case ArchiveType.GZip:
return GZipArchive.Open(stream, readerOptions);
case ArchiveType.Rar:
return RarArchive.Open(stream, readerOptions);
case ArchiveType.Tar:
return TarArchive.Open(stream, readerOptions);
}
}
throw new InvalidOperationException("Cannot determine compressed stream type. Supported Archive Formats: Zip, GZip, Tar, Rar, 7Zip, LZip");
return FindArchiveFactory(stream).Open(stream, readerOptions);
}

public static IWritableArchive Create(ArchiveType type)
Expand Down Expand Up @@ -84,29 +96,7 @@ public static IArchive Open(FileInfo fileInfo, ReaderOptions? options = null)
fileInfo.CheckNotNull(nameof(fileInfo));
options ??= new ReaderOptions { LeaveStreamOpen = false };

ArchiveType? type;
using (Stream stream = fileInfo.OpenRead())
{
IsArchive(stream, out type); //test and reset stream position

if (type != null)
{
switch (type.Value)
{
case ArchiveType.Zip:
return ZipArchive.Open(fileInfo, options);
case ArchiveType.SevenZip:
return SevenZipArchive.Open(fileInfo, options);
case ArchiveType.GZip:
return GZipArchive.Open(fileInfo, options);
case ArchiveType.Rar:
return RarArchive.Open(fileInfo, options);
case ArchiveType.Tar:
return TarArchive.Open(fileInfo, options);
}
}
}
throw new InvalidOperationException("Cannot determine compressed stream type. Supported Archive Formats: Zip, GZip, Tar, Rar, 7Zip");
return FindArchiveFactory(fileInfo).Open(fileInfo, options);
}

/// <summary>
Expand All @@ -124,31 +114,10 @@ public static IArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions? opti
if (files.Length == 1)
return Open(fileInfo, options);


fileInfo.CheckNotNull(nameof(fileInfo));
options ??= new ReaderOptions { LeaveStreamOpen = false };

ArchiveType? type;
using (Stream stream = fileInfo.OpenRead())
IsArchive(stream, out type); //test and reset stream position

if (type != null)
{
switch (type.Value)
{
case ArchiveType.Zip:
return ZipArchive.Open(files, options);
case ArchiveType.SevenZip:
return SevenZipArchive.Open(files, options);
case ArchiveType.GZip:
return GZipArchive.Open(files, options);
case ArchiveType.Rar:
return RarArchive.Open(files, options);
case ArchiveType.Tar:
return TarArchive.Open(files, options);
}
}
throw new InvalidOperationException("Cannot determine compressed stream type. Supported Archive Formats: Zip, GZip, Tar, Rar, 7Zip");
return FindArchiveFactory(fileInfo).Open(fileInfos, options);
}

/// <summary>
Expand All @@ -159,36 +128,18 @@ public static IArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions? opti
public static IArchive Open(IEnumerable<Stream> streams, ReaderOptions? options = null)
{
streams.CheckNotNull(nameof(streams));
if (streams.Count() == 0)
Stream[] streamsArray = streams.ToArray();
if (streamsArray.Length == 0)
throw new InvalidOperationException("No streams");
if (streams.Count() == 1)
return Open(streams.First(), options);

Stream firstStream = streamsArray[0];
if (streamsArray.Length == 1)
return Open(firstStream, options);

firstStream.CheckNotNull(nameof(firstStream));
options ??= new ReaderOptions();

ArchiveType? type;
using (Stream stream = streams.First())
IsArchive(stream, out type); //test and reset stream position

if (type != null)
{
switch (type.Value)
{
case ArchiveType.Zip:
return ZipArchive.Open(streams, options);
case ArchiveType.SevenZip:
return SevenZipArchive.Open(streams, options);
case ArchiveType.GZip:
return GZipArchive.Open(streams, options);
case ArchiveType.Rar:
return RarArchive.Open(streams, options);
case ArchiveType.Tar:
return TarArchive.Open(streams, options);
}
}
throw new InvalidOperationException("Cannot determine compressed stream type. Supported Archive Formats: Zip, GZip, Tar, Rar, 7Zip");
}
return FindArchiveFactory(firstStream).Open(streamsArray, options);
}

/// <summary>
/// Extract to specific directory, retaining filename
Expand All @@ -203,6 +154,44 @@ public static IArchive Open(IEnumerable<Stream> streams, ReaderOptions? options
}
}

private static IArchiveFactory FindArchiveFactory(FileInfo finfo)
{
finfo.CheckNotNull(nameof(finfo));
using (Stream stream = finfo.OpenRead())
{
return FindArchiveFactory(stream);
}
}

private static IArchiveFactory FindArchiveFactory(Stream stream)
{
stream.CheckNotNull(nameof(stream));
if (!stream.CanRead || !stream.CanSeek)
{
throw new ArgumentException("Stream should be readable and seekable");
}

long startPosition = stream.Position;

foreach (var factory in Factories)
{
stream.Seek(startPosition, SeekOrigin.Begin);

if (factory.IsArchive(stream))
{
stream.Seek(startPosition, SeekOrigin.Begin);

return factory;
}
}

var extensions = Factories
.Select(item => item.Name)
.Aggregate((a, b) => a + ", " + b);

throw new InvalidOperationException($"Cannot determine compressed stream type. Supported Archive Formats: {extensions}");
}

public static bool IsArchive(string filePath, out ArchiveType? type)
{
filePath.CheckNotNullOrEmpty(nameof(filePath));
Expand Down
51 changes: 51 additions & 0 deletions src/SharpCompress/Archives/GZip/GZipArchiveFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

using SharpCompress.Readers;

namespace SharpCompress.Archives.GZip
{
public class GZipArchiveFactory : IArchiveFactory
{
/// <inheritdoc/>
public string Name => "GZip";

/// <inheritdoc/>
public IEnumerable<string> GetSupportedExtensions()
{
yield return "gz";
}

/// <inheritdoc/>
public bool IsArchive(Stream stream, string? password = null)
{
return GZipArchive.IsGZipFile(stream);
}

/// <inheritdoc/>
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
return GZipArchive.Open(stream, readerOptions);
}

/// <inheritdoc/>
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
return GZipArchive.Open(fileInfo, readerOptions);
}

/// <inheritdoc/>
public IArchive Open(IEnumerable<Stream> streams, ReaderOptions? readerOptions = null)
{
return GZipArchive.Open(streams, readerOptions);
}

/// <inheritdoc/>
public IArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions? readerOptions = null)
{
return GZipArchive.Open(fileInfos, readerOptions);
}
}
}
71 changes: 71 additions & 0 deletions src/SharpCompress/Archives/IArchiveFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.IO;

using SharpCompress.Common;
using SharpCompress.Readers;

namespace SharpCompress.Archives
{
/// <summary>
/// Represents a factory used to identify and open archives.
/// </summary>
/// <remarks>
/// Currently implemented by:<br/>
/// <list type="table">
/// <item><see cref="Tar.TarArchiveFactory"/></item>
/// <item><see cref="Rar.RarArchiveFactory"/></item>
/// <item><see cref="Zip.ZipArchiveFactory"/></item>
/// <item><see cref="GZip.GZipArchiveFactory"/></item>
/// <item><see cref="SevenZip.SevenZipArchiveFactory"/></item>
/// </list>
/// </remarks>
public interface IArchiveFactory
{
/// <summary>
/// Gets the archive Type name
/// </summary>
string Name { get; }

/// <summary>
/// returns the extensions typically used by this archive type.
/// </summary>
/// <returns></returns>
IEnumerable<string> GetSupportedExtensions();

/// <summary>
/// Returns true if the stream represents an archive of the format defined by this type.
/// </summary>
/// <param name="stream"></param>
/// /// <param name="password">optional password</param>
bool IsArchive(Stream stream, string? password = null);

/// <summary>
/// Opens an Archive for random access.
/// </summary>
/// <param name="stream">An open, readable and seekable stream.</param>
/// <param name="readerOptions">reading options.</param>
IArchive Open(Stream stream, ReaderOptions? readerOptions = null);

/// <summary>
/// Constructor with a FileInfo object to an existing file.
/// </summary>
/// <param name="fileInfo">the file to open.</param>
/// <param name="readerOptions">reading options.</param>
IArchive Open(System.IO.FileInfo fileInfo, ReaderOptions? readerOptions = null);

/// <summary>
/// Constructor with IEnumerable FileInfo objects, multi and split support.
/// </summary>
/// <param name="streams"></param>
/// <param name="readerOptions">reading options.</param>
IArchive Open(IEnumerable<Stream> streams, ReaderOptions? readerOptions = null);

/// <summary>
/// Constructor with IEnumerable Stream objects, multi and split support.
/// </summary>
/// <param name="fileInfos"></param>
/// <param name="readerOptions">reading options.</param>
IArchive Open(IEnumerable<System.IO.FileInfo> fileInfos, ReaderOptions? readerOptions = null);
}
}
52 changes: 52 additions & 0 deletions src/SharpCompress/Archives/Rar/RarArchiveFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

using SharpCompress.Readers;

namespace SharpCompress.Archives.Rar
{
public class RarArchiveFactory : IArchiveFactory
{
/// <inheritdoc/>
public string Name => "Rar";

/// <inheritdoc/>
public IEnumerable<string> GetSupportedExtensions()
{
yield return "rar";
yield return "cbr";
}

/// <inheritdoc/>
public bool IsArchive(Stream stream, string? password = null)
{
return RarArchive.IsRarFile(stream);
}

/// <inheritdoc/>
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
return RarArchive.Open(stream, readerOptions);
}

/// <inheritdoc/>
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
return RarArchive.Open(fileInfo, readerOptions);
}

/// <inheritdoc/>
public IArchive Open(IEnumerable<Stream> streams, ReaderOptions? readerOptions = null)
{
return RarArchive.Open(streams, readerOptions);
}

/// <inheritdoc/>
public IArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions? readerOptions = null)
{
return RarArchive.Open(fileInfos, readerOptions);
}
}
}
Loading