diff --git a/src/ImageSharp/Bootstrapper.cs b/src/ImageSharp/Bootstrapper.cs index 3c91f2274..de4bcc938 100644 --- a/src/ImageSharp/Bootstrapper.cs +++ b/src/ImageSharp/Bootstrapper.cs @@ -64,9 +64,41 @@ namespace ImageSharp /// The new format to add. public void AddImageFormat(IImageFormat format) { - if (this.imageFormats.All(i => i.GetType() != format.GetType())) + Guard.NotNull(format, nameof(format)); + Guard.NotNull(format.Encoder, nameof(format), "The encoder should not be null."); + Guard.NotNull(format.Decoder, nameof(format), "The decoder should not be null."); + Guard.NotNullOrEmpty(format.MimeType, nameof(format), "The mime type should not be null or empty."); + Guard.NotNullOrEmpty(format.Extension, nameof(format), "The extension should not be null or empty."); + Guard.NotNullOrEmpty(format.SupportedExtensions, nameof(format), "The supported extensions not be null or empty."); + + GuardDuplicate(format); + + this.imageFormats.Add(format); + } + + private void GuardDuplicate(IImageFormat format) + { + if (!format.SupportedExtensions.Contains(format.Extension, StringComparer.OrdinalIgnoreCase)) + { + throw new ArgumentException("The supported extensions should contain the default extension.", nameof(format)); + } + + if (format.SupportedExtensions.Any(e => string.IsNullOrWhiteSpace(e))) + { + throw new ArgumentException("The supported extensions should not contain empty values.", nameof(format)); + } + + foreach (var imageFormat in this.imageFormats) { - this.imageFormats.Add(format); + if (imageFormat.Extension.Equals(format.Extension, StringComparison.OrdinalIgnoreCase)) + { + throw new ArgumentException("There is already a format with the same extension.", nameof(format)); + } + + if (imageFormat.SupportedExtensions.Intersect(format.SupportedExtensions, StringComparer.OrdinalIgnoreCase).Any()) + { + throw new ArgumentException("There is already a format that supports the same extension.", nameof(format)); + } } } } diff --git a/src/ImageSharp/Common/Helpers/Guard.cs b/src/ImageSharp/Common/Helpers/Guard.cs index ecb497d3e..b67512525 100644 --- a/src/ImageSharp/Common/Helpers/Guard.cs +++ b/src/ImageSharp/Common/Helpers/Guard.cs @@ -6,7 +6,9 @@ namespace ImageSharp { using System; + using System.Collections.Generic; using System.Diagnostics; + using System.Linq; /// /// Provides methods to protect against invalid parameters. @@ -42,21 +44,47 @@ namespace ImageSharp /// /// The target string, which should be checked against being null or empty. /// Name of the parameter. + /// The error message, if any to add to the exception. /// is null. /// is empty or contains only blanks. - public static void NotNullOrEmpty(string target, string parameterName) + public static void NotNullOrEmpty(string target, string parameterName, string message = "") { - if (target == null) - { - throw new ArgumentNullException(parameterName); - } + NotNull(target, parameterName, message); if (string.IsNullOrWhiteSpace(target)) { + if (!string.IsNullOrWhiteSpace(message)) + { + throw new ArgumentException(message, parameterName); + } + throw new ArgumentException("Value cannot be null or empty and cannot contain only blanks.", parameterName); } } + /// + /// Verifies, that the enumeration is not null and not empty. + /// + /// The target enumeration, which should be checked against being null or empty. + /// Name of the parameter. + /// The error message, if any to add to the exception. + /// is null. + /// is empty. + public static void NotNullOrEmpty(IEnumerable target, string parameterName, string message = "") + { + NotNull(target, parameterName, message); + + if (!target.Any()) + { + if (!string.IsNullOrWhiteSpace(message)) + { + throw new ArgumentException(message, parameterName); + } + + throw new ArgumentException("Value cannot be empty.", parameterName); + } + } + /// /// Verifies that the specified value is less than a maximum value /// and throws an exception if it is not. diff --git a/tests/ImageSharp.Tests/BootstrapperTests.cs b/tests/ImageSharp.Tests/BootstrapperTests.cs new file mode 100644 index 000000000..4c8ca7f8f --- /dev/null +++ b/tests/ImageSharp.Tests/BootstrapperTests.cs @@ -0,0 +1,160 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.Collections.Generic; + using ImageSharp.Formats; + using Xunit; + using System.Linq; + + public class BootstrapperTests + { + private class TestFormat : IImageFormat + { + private IImageDecoder decoder; + private IImageEncoder encoder; + private string mimeType; + private string extension; + private IEnumerable supportedExtensions; + + public TestFormat() + { + this.decoder = new JpegDecoder(); + this.encoder = new JpegEncoder(); + this.extension = "jpg"; + this.mimeType = "image/test"; + this.supportedExtensions = new string[] { "jpg" }; + } + + public IImageDecoder Decoder { get { return this.decoder; } set { this.decoder = value; } } + + public IImageEncoder Encoder { get { return this.encoder; } set { this.encoder = value; } } + + public string MimeType { get { return this.mimeType; } set { this.mimeType = value; } } + + public string Extension { get { return this.extension; } set { this.extension = value; } } + + public IEnumerable SupportedExtensions { get { return this.supportedExtensions; } set { this.supportedExtensions = value; } } + } + + [Fact] + public void AddImageFormatGuardNull() + { + ArgumentException exception; + + exception = Assert.Throws(() => + { + Bootstrapper.Instance.AddImageFormat(null); + }); + + var format = new TestFormat(); + format.Decoder = null; + + exception = Assert.Throws(() => + { + Bootstrapper.Instance.AddImageFormat(format); + }); + Assert.Contains("decoder", exception.Message); + + format = new TestFormat(); + format.Encoder = null; + + exception = Assert.Throws(() => + { + Bootstrapper.Instance.AddImageFormat(format); + }); + Assert.Contains("encoder", exception.Message); + + format = new TestFormat(); + format.MimeType = null; + + exception = Assert.Throws(() => + { + Bootstrapper.Instance.AddImageFormat(format); + }); + Assert.Contains("mime type", exception.Message); + + format = new TestFormat(); + format.MimeType = ""; + + exception = Assert.Throws(() => + { + Bootstrapper.Instance.AddImageFormat(format); + }); + Assert.Contains("mime type", exception.Message); + + format = new TestFormat(); + format.Extension = null; + + exception = Assert.Throws(() => + { + Bootstrapper.Instance.AddImageFormat(format); + }); + Assert.Contains("extension", exception.Message); + + format = new TestFormat(); + format.Extension = ""; + + exception = Assert.Throws(() => + { + Bootstrapper.Instance.AddImageFormat(format); + }); + Assert.Contains("extension", exception.Message); + + format = new TestFormat(); + format.SupportedExtensions = null; + + exception = Assert.Throws(() => + { + Bootstrapper.Instance.AddImageFormat(format); + }); + Assert.Contains("supported extensions", exception.Message); + + format = new TestFormat(); + format.SupportedExtensions = Enumerable.Empty(); + + exception = Assert.Throws(() => + { + Bootstrapper.Instance.AddImageFormat(format); + }); + Assert.Contains("supported extensions", exception.Message); + } + + [Fact] + public void AddImageFormatChecks() + { + var format = new TestFormat(); + + var exception = Assert.Throws(() => + { + Bootstrapper.Instance.AddImageFormat(format); + }); + Assert.Contains("format with the same", exception.Message); + + format.Extension = "test"; + exception = Assert.Throws(() => + { + Bootstrapper.Instance.AddImageFormat(format); + }); + Assert.Contains("should contain", exception.Message); + + format.SupportedExtensions = new string[] { "test", "jpg" }; + exception = Assert.Throws(() => + { + Bootstrapper.Instance.AddImageFormat(format); + }); + Assert.Contains("supports the same", exception.Message); + + format.SupportedExtensions = new string[] { "test", "" }; + exception = Assert.Throws(() => + { + Bootstrapper.Instance.AddImageFormat(format); + }); + Assert.Contains("empty values", exception.Message); + } + } +}