Skip to content

Commit

Permalink
Merge pull request #671 from vpenades/master
Browse files Browse the repository at this point in the history
Introduced IArchiveFactory
  • Loading branch information
adamhathcock committed Sep 5, 2022
2 parents 4ba2963 + 6e4f54e commit f1d8fad
Show file tree
Hide file tree
Showing 7 changed files with 419 additions and 91 deletions.
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

0 comments on commit f1d8fad

Please sign in to comment.