From 032c02ccecf405d658cd19a725fc187705d3292f Mon Sep 17 00:00:00 2001 From: Donald Gray Date: Mon, 17 Jul 2023 10:01:05 +0100 Subject: [PATCH] Switch to file-scoped namespaces, add global using --- .../AccessTokenErrorTests.cs | 41 +- .../AccessTokenResponseTests.cs | 53 +- .../Auth/V2/ContentResourceWithAuthTest.cs | 33 +- .../Auth/V2/ImageServiceWithAuthTest.cs | 67 +- .../IIIF.Tests/Auth/V2/ProbeServiceTests.cs | 217 +++-- src/IIIF/IIIF.Tests/Auth/V2/ReusableParts.cs | 43 +- .../IIIF.Tests/Auth/V2/TokenServiceTests.cs | 65 +- src/IIIF/IIIF.Tests/IIIF.Tests.csproj | 8 +- .../IIIF.Tests/ImageApi/ImageRequestXTests.cs | 409 +++++---- .../IIIF.Tests/ImageApi/SizeParameterTests.cs | 409 +++++---- src/IIIF/IIIF.Tests/ImageApi/SizeTests.cs | 780 +++++++++--------- .../ImageApi/V2/ImageService2Tests.cs | 51 +- src/IIIF/IIIF.Tests/ImageApi/VersionTests.cs | 29 +- .../Presentation/V2/MetadataValueTests.cs | 245 +++--- .../Presentation/V2/MetadataXTests.cs | 149 ++-- .../MetaDataValueSerialiserTests.cs | 358 ++++---- .../IIIF.Tests/Presentation/V3/CanvasTests.cs | 191 +++-- .../Presentation/V3/ManifestXTests.cs | 159 ++-- .../IIIF.Tests/Presentation/VersionTests.cs | 27 +- .../Serialisation/DiscoveryTests.cs | 187 +++-- .../ImageService2SerialiserTests.cs | 411 +++++---- .../ImageService3SerialiserTests.cs | 261 +++--- .../LanguageMapSerialiserTests.cs | 134 +-- .../ManifestSerialisationTests.cs | 271 +++--- .../Serialisation/MixedAuthServicesTest.cs | 76 +- .../MixedVersionSerialisertests.cs | 57 +- .../ObjectIfSingleConverterTests.cs | 26 +- .../PrettyIIIFContractResolverTests.cs | 363 ++++---- .../Serialisation/TargetConverterTests.cs | 116 +-- .../XsdDateTimeConverterTests.cs | 89 +- src/IIIF/IIIF.Tests/StringAssertionX.cs | 19 +- .../V0/AccessTokenService/AccessTokenError.cs | 123 ++- .../AccessTokenService/AccessTokenResponse.cs | 35 +- src/IIIF/IIIF/Auth/V0/AuthCookieService.cs | 84 +- src/IIIF/IIIF/Auth/V0/AuthLogoutService.cs | 30 +- src/IIIF/IIIF/Auth/V0/AuthTokenService.cs | 30 +- src/IIIF/IIIF/Auth/V0/Constants.cs | 9 +- .../V1/AccessTokenService/AccessTokenError.cs | 123 ++- .../AccessTokenService/AccessTokenResponse.cs | 35 +- src/IIIF/IIIF/Auth/V1/AuthCookieService.cs | 84 +- src/IIIF/IIIF/Auth/V1/AuthLogoutService.cs | 30 +- src/IIIF/IIIF/Auth/V1/AuthTokenService.cs | 30 +- src/IIIF/IIIF/Auth/V1/Constants.cs | 9 +- src/IIIF/IIIF/Auth/V2/AuthAccessService2.cs | 32 +- src/IIIF/IIIF/Auth/V2/AuthAccessToken2.cs | 36 +- .../IIIF/Auth/V2/AuthAccessTokenError2.cs | 51 +- .../IIIF/Auth/V2/AuthAccessTokenService2.cs | 22 +- src/IIIF/IIIF/Auth/V2/AuthLogoutService2.cs | 9 +- src/IIIF/IIIF/Auth/V2/AuthProbeResult2.cs | 49 +- src/IIIF/IIIF/Auth/V2/AuthProbeService2.cs | 23 +- src/IIIF/IIIF/Auth/V2/Constants.cs | 9 +- src/IIIF/IIIF/Discovery/ContentTypes.cs | 17 +- src/IIIF/IIIF/Discovery/Context.cs | 9 +- src/IIIF/IIIF/Discovery/V1/Activity.cs | 245 +++--- .../IIIF/Discovery/V1/OrderedCollection.cs | 96 ++- .../Discovery/V1/OrderedCollectionPage.cs | 82 +- src/IIIF/IIIF/IIIF.csproj | 10 +- src/IIIF/IIIF/IService.cs | 13 +- src/IIIF/IIIF/ImageApi/ContentTypes.cs | 25 +- src/IIIF/IIIF/ImageApi/ImageRequest.cs | 115 ++- src/IIIF/IIIF/ImageApi/ImageRequestX.cs | 54 +- src/IIIF/IIIF/ImageApi/RegionParameter.cs | 94 +-- src/IIIF/IIIF/ImageApi/RotationParameter.cs | 38 +- src/IIIF/IIIF/ImageApi/SizeParameter.cs | 154 ++-- src/IIIF/IIIF/ImageApi/Tile.cs | 20 +- src/IIIF/IIIF/ImageApi/V2/Image2Profile.cs | 11 +- src/IIIF/IIIF/ImageApi/V2/ImageService2.cs | 70 +- .../IIIF/ImageApi/V2/ProfileDescription.cs | 109 ++- src/IIIF/IIIF/ImageApi/V2/Supports.cs | 193 +++-- src/IIIF/IIIF/ImageApi/V3/Features.cs | 187 +++-- src/IIIF/IIIF/ImageApi/V3/ImageService3.cs | 100 ++- src/IIIF/IIIF/ImageApi/Version.cs | 42 +- src/IIIF/IIIF/JsonLdBase.cs | 20 +- src/IIIF/IIIF/Presentation/ContentTypes.cs | 25 +- src/IIIF/IIIF/Presentation/Context.cs | 143 ++-- .../Presentation/V2/Annotation/Annotation.cs | 39 +- .../V2/Annotation/AnnotationList.cs | 17 +- .../V2/Annotation/AnnotationListReference.cs | 19 +- .../ContentAsTextAnnotationResource.cs | 23 +- .../V2/Annotation/IAnnotationListReference.cs | 11 +- .../IllustrationAnnotationResource.cs | 9 +- .../V2/Annotation/ImageAnnotation.cs | 27 +- .../V2/Annotation/SearchResultAnnotation.cs | 9 +- .../SearchResultAnnotationResource.cs | 19 +- src/IIIF/IIIF/Presentation/V2/Canvas.cs | 29 +- src/IIIF/IIIF/Presentation/V2/Collection.cs | 37 +- .../IIIF/Presentation/V2/ExternalResource.cs | 23 +- .../Presentation/V2/IIIFPresentationBase.cs | 99 ++- .../IIIF/Presentation/V2/ImageResource.cs | 23 +- src/IIIF/IIIF/Presentation/V2/Manifest.cs | 39 +- src/IIIF/IIIF/Presentation/V2/Metadata.cs | 23 +- src/IIIF/IIIF/Presentation/V2/MetadataX.cs | 29 +- src/IIIF/IIIF/Presentation/V2/Range.cs | 39 +- src/IIIF/IIIF/Presentation/V2/Resource.cs | 19 +- src/IIIF/IIIF/Presentation/V2/ResourceBase.cs | 38 +- src/IIIF/IIIF/Presentation/V2/Sequence.cs | 27 +- .../Serialisation/MetaDataValueSerialiser.cs | 137 ++- .../Presentation/V2/Strings/LanguageValue.cs | 18 +- .../Presentation/V2/Strings/MetaDataValue.cs | 95 ++- src/IIIF/IIIF/Presentation/V2/Thumbnail.cs | 15 +- src/IIIF/IIIF/Presentation/V2/ValueObject.cs | 20 +- src/IIIF/IIIF/Presentation/V3/Agent.cs | 16 +- .../Presentation/V3/Annotation/Annotation.cs | 44 +- .../V3/Annotation/AnnotationCollection.cs | 11 +- .../V3/Annotation/AnnotationPage.cs | 16 +- .../V3/Annotation/ClassifyingBody.cs | 15 +- .../Presentation/V3/Annotation/IAnnotation.cs | 7 +- .../Presentation/V3/Annotation/IPaintable.cs | 22 +- .../V3/Annotation/PaintingAnnotation.cs | 16 +- .../V3/Annotation/PaintingChoice.cs | 15 +- .../V3/Annotation/SupplementingAnnotation.cs | 13 +- .../Presentation/V3/Annotation/TextualBody.cs | 33 +- .../Annotation/TypeClassifyingAnnotation.cs | 29 +- src/IIIF/IIIF/Presentation/V3/Canvas.cs | 69 +- src/IIIF/IIIF/Presentation/V3/Collection.cs | 55 +- .../Presentation/V3/Constants/Behavior.cs | 51 +- .../Presentation/V3/Constants/Motivation.cs | 196 +++-- .../Presentation/V3/Constants/Services.cs | 29 +- .../Presentation/V3/Constants/TimeMode.cs | 16 +- .../V3/Constants/ViewingDirection.cs | 49 +- .../IIIF/Presentation/V3/Content/Audio.cs | 11 +- .../V3/Content/ExternalResource.cs | 46 +- .../IIIF/Presentation/V3/Content/ISpatial.cs | 19 +- .../IIIF/Presentation/V3/Content/ITemporal.cs | 17 +- .../IIIF/Presentation/V3/Content/Image.cs | 19 +- .../IIIF/Presentation/V3/Content/Video.cs | 15 +- .../IIIF/Presentation/V3/ICollectionItem.cs | 17 +- .../Presentation/V3/IStructuralLocation.cs | 18 +- src/IIIF/IIIF/Presentation/V3/Manifest.cs | 57 +- src/IIIF/IIIF/Presentation/V3/ManifestX.cs | 21 +- src/IIIF/IIIF/Presentation/V3/Range.cs | 31 +- src/IIIF/IIIF/Presentation/V3/ResourceBase.cs | 232 +++--- .../V3/Selectors/AudioContentSelector.cs | 12 +- .../Presentation/V3/Selectors/ISelector.cs | 9 +- .../V3/Selectors/ImageApiSelector.cs | 22 +- .../V3/Selectors/PointSelector.cs | 17 +- .../V3/Selectors/VideoContentSelector.cs | 12 +- .../IIIF/Presentation/V3/SpecificResource.cs | 21 +- .../Presentation/V3/Strings/LabelValuePair.cs | 72 +- .../Presentation/V3/Strings/LanguageMap.cs | 61 +- .../IIIF/Presentation/V3/StructureBase.cs | 88 +- src/IIIF/IIIF/Presentation/Version.cs | 42 +- .../IIIF/Search/IHasIgnorableParameters.cs | 11 +- .../IIIF/Search/V1/AutoCompleteService.cs | 15 +- src/IIIF/IIIF/Search/V1/Hit.cs | 37 +- .../Search/V1/SearchResultAnnotationList.cs | 29 +- src/IIIF/IIIF/Search/V1/SearchResultsLayer.cs | 41 +- src/IIIF/IIIF/Search/V1/SearchService.cs | 20 +- src/IIIF/IIIF/Search/V1/Term.cs | 27 +- src/IIIF/IIIF/Search/V1/TermList.cs | 23 +- src/IIIF/IIIF/Search/V1/TextQuoteSelector.cs | 29 +- .../IIIF/Search/V2/AutoCompleteService.cs | 17 +- src/IIIF/IIIF/Search/V2/SearchService.cs | 19 +- .../Serialisation/CamelCaseEnumAttribute.cs | 35 +- .../Deserialisation/AnnotationV3Converter.cs | 39 +- .../ExternalResourceConverter.cs | 39 +- .../Deserialisation/PaintableConverter.cs | 41 +- .../Deserialisation/ReadOnlyConverter.cs | 14 +- .../ResourceBaseV3Converter.cs | 105 ++- .../Deserialisation/SelectorConverter.cs | 37 +- .../Deserialisation/ServiceConverter.cs | 129 ++- .../StructuralLocationConverter.cs | 37 +- .../EnumCamelCaseValueConverter.cs | 84 +- .../Serialisation/EnumStringValueConverter.cs | 25 +- src/IIIF/IIIF/Serialisation/EnumX.cs | 29 +- .../IIIF/Serialisation/IIIFSerialiserX.cs | 147 ++-- .../Serialisation/ImageService2Converter.cs | 141 ++-- .../Serialisation/JsonSerializerExtensions.cs | 91 +- .../Serialisation/LanguageMapSerialiser.cs | 72 +- .../Serialisation/ObjectIfSingleAttribute.cs | 17 +- .../Serialisation/ObjectIfSingleConverter.cs | 134 ++- .../PrettyIIIFContractResolver.cs | 107 ++- .../Serialisation/RequiredOutputAttribute.cs | 17 +- .../ServiceReferenceConverter.cs | 27 +- src/IIIF/IIIF/Serialisation/SizeConverter.cs | 17 +- .../Serialisation/StringArrayConverter.cs | 46 +- .../IIIF/Serialisation/TargetConverter.cs | 75 +- .../IIIF/Serialisation/ThumbnailConverter.cs | 53 +- .../IIIF/Serialisation/WriteOnlyConverter.cs | 35 +- .../Serialisation/XsdDateTimeConverter.cs | 21 +- src/IIIF/IIIF/Size.cs | 354 ++++---- src/IIIF/IIIF/Usings.cs | 1 + src/IIIF/IIIF/Utils/CollectionX.cs | 9 +- src/IIIF/IIIF/V2ServiceReference.cs | 31 +- 184 files changed, 6225 insertions(+), 6565 deletions(-) create mode 100644 src/IIIF/IIIF/Usings.cs diff --git a/src/IIIF/IIIF.Tests/Auth/V1/AccessTokenService/AccessTokenErrorTests.cs b/src/IIIF/IIIF.Tests/Auth/V1/AccessTokenService/AccessTokenErrorTests.cs index 9c6dd19..c6ed96b 100644 --- a/src/IIIF/IIIF.Tests/Auth/V1/AccessTokenService/AccessTokenErrorTests.cs +++ b/src/IIIF/IIIF.Tests/Auth/V1/AccessTokenService/AccessTokenErrorTests.cs @@ -2,29 +2,28 @@ using IIIF.Auth.V1.AccessTokenService; using Xunit; -namespace IIIF.Tests.Auth.V1.AccessTokenService +namespace IIIF.Tests.Auth.V1.AccessTokenService; + +public class AccessTokenErrorTests { - public class AccessTokenErrorTests + [Theory] + [InlineData(AccessTokenErrorConditions.InvalidRequest, + "The service could not process the information sent in the body of the request.")] + [InlineData(AccessTokenErrorConditions.MissingCredentials, + "The request did not have the credentials required.")] + [InlineData(AccessTokenErrorConditions.InvalidCredentials, + "The request had credentials that are not valid for the service.")] + [InlineData(AccessTokenErrorConditions.InvalidOrigin, + "The request came from a different origin than that specified in the access cookie service request, or an origin that the server rejects for other reasons.")] + [InlineData(AccessTokenErrorConditions.Unavailable, + "The request could not be fulfilled for reasons other than those listed above, such as scheduled maintenance.")] + public void Ctor_ErrorOnly_UsesDefaultDescription(AccessTokenErrorConditions error, string description) { - [Theory] - [InlineData(AccessTokenErrorConditions.InvalidRequest, - "The service could not process the information sent in the body of the request.")] - [InlineData(AccessTokenErrorConditions.MissingCredentials, - "The request did not have the credentials required.")] - [InlineData(AccessTokenErrorConditions.InvalidCredentials, - "The request had credentials that are not valid for the service.")] - [InlineData(AccessTokenErrorConditions.InvalidOrigin, - "The request came from a different origin than that specified in the access cookie service request, or an origin that the server rejects for other reasons.")] - [InlineData(AccessTokenErrorConditions.Unavailable, - "The request could not be fulfilled for reasons other than those listed above, such as scheduled maintenance.")] - public void Ctor_ErrorOnly_UsesDefaultDescription(AccessTokenErrorConditions error, string description) - { - // Arrange - var accessTokenError = new AccessTokenError(error); + // Arrange + var accessTokenError = new AccessTokenError(error); - // Assert - accessTokenError.Error.Should().Be(error); - accessTokenError.Description.Should().Be(description); - } + // Assert + accessTokenError.Error.Should().Be(error); + accessTokenError.Description.Should().Be(description); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Auth/V1/AccessTokenService/AccessTokenResponseTests.cs b/src/IIIF/IIIF.Tests/Auth/V1/AccessTokenService/AccessTokenResponseTests.cs index f1fec5e..a29dac6 100644 --- a/src/IIIF/IIIF.Tests/Auth/V1/AccessTokenService/AccessTokenResponseTests.cs +++ b/src/IIIF/IIIF.Tests/Auth/V1/AccessTokenService/AccessTokenResponseTests.cs @@ -4,38 +4,37 @@ using Newtonsoft.Json; using Xunit; -namespace IIIF.Tests.Auth.V1.AccessTokenService +namespace IIIF.Tests.Auth.V1.AccessTokenService; + +public class AccessTokenResponseTests { - public class AccessTokenResponseTests - { - private readonly JsonSerializerSettings jsonSerializerSettings; + private readonly JsonSerializerSettings jsonSerializerSettings; - public AccessTokenResponseTests() + public AccessTokenResponseTests() + { + // NOTE: Using JsonSerializerSettings to facilitate testing as it makes it a LOT easier + jsonSerializerSettings = new JsonSerializerSettings { - // NOTE: Using JsonSerializerSettings to facilitate testing as it makes it a LOT easier - jsonSerializerSettings = new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - ContractResolver = new PrettyIIIFContractResolver(), - }; - } + NullValueHandling = NullValueHandling.Ignore, + ContractResolver = new PrettyIIIFContractResolver() + }; + } - [Theory] - [InlineData(AccessTokenErrorConditions.InvalidRequest, "invalidRequest")] - [InlineData(AccessTokenErrorConditions.MissingCredentials, "missingCredentials")] - [InlineData(AccessTokenErrorConditions.InvalidCredentials, "invalidCredentials")] - [InlineData(AccessTokenErrorConditions.InvalidOrigin, "invalidOrigin")] - [InlineData(AccessTokenErrorConditions.Unavailable, "unavailable")] - public void AccessTokenError_ReturnsExpectedJson(AccessTokenErrorConditions error, string description) - { - // Arrange - var accessTokenError = new AccessTokenError(error, "test description"); - - var expected = $"{{\"error\":\"{description}\",\"description\":\"test description\"}}"; + [Theory] + [InlineData(AccessTokenErrorConditions.InvalidRequest, "invalidRequest")] + [InlineData(AccessTokenErrorConditions.MissingCredentials, "missingCredentials")] + [InlineData(AccessTokenErrorConditions.InvalidCredentials, "invalidCredentials")] + [InlineData(AccessTokenErrorConditions.InvalidOrigin, "invalidOrigin")] + [InlineData(AccessTokenErrorConditions.Unavailable, "unavailable")] + public void AccessTokenError_ReturnsExpectedJson(AccessTokenErrorConditions error, string description) + { + // Arrange + var accessTokenError = new AccessTokenError(error, "test description"); + + var expected = $"{{\"error\":\"{description}\",\"description\":\"test description\"}}"; - var actual = JsonConvert.SerializeObject(accessTokenError, jsonSerializerSettings); + var actual = JsonConvert.SerializeObject(accessTokenError, jsonSerializerSettings); - actual.Should().Be(expected); - } + actual.Should().Be(expected); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Auth/V2/ContentResourceWithAuthTest.cs b/src/IIIF/IIIF.Tests/Auth/V2/ContentResourceWithAuthTest.cs index 052eed2..6edb128 100644 --- a/src/IIIF/IIIF.Tests/Auth/V2/ContentResourceWithAuthTest.cs +++ b/src/IIIF/IIIF.Tests/Auth/V2/ContentResourceWithAuthTest.cs @@ -3,28 +3,27 @@ using IIIF.Serialisation; using Xunit; -namespace IIIF.Tests.Auth.V2 +namespace IIIF.Tests.Auth.V2; + +public class ContentResourceWithAuthTest { - public class ContentResourceWithAuthTest + [Fact] + public void ContentResource_Can_Have_Auth_Services() { - [Fact] - public void ContentResource_Can_Have_Auth_Services() + // Arrange + var res = new ExternalResource("Text") { - // Arrange - var res = new ExternalResource("Text") - { - Id = "https://example.com/documents/my.pdf", - Service = ReusableParts.Auth2Services - }; - - // Act - var json = res.AsJson().Replace("\r\n", "\n"); - const string expected = @"{ + Id = "https://example.com/documents/my.pdf", + Service = ReusableParts.Auth2Services + }; + + // Act + var json = res.AsJson().Replace("\r\n", "\n"); + const string expected = @"{ ""id"": ""https://example.com/documents/my.pdf"", ""type"": ""Text""," + ReusableParts.ExpectedServiceAsArray + @" }"; - // Assert - json.Should().BeEquivalentTo(expected); - } + // Assert + json.Should().BeEquivalentTo(expected); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Auth/V2/ImageServiceWithAuthTest.cs b/src/IIIF/IIIF.Tests/Auth/V2/ImageServiceWithAuthTest.cs index a08e395..aefc1e2 100644 --- a/src/IIIF/IIIF.Tests/Auth/V2/ImageServiceWithAuthTest.cs +++ b/src/IIIF/IIIF.Tests/Auth/V2/ImageServiceWithAuthTest.cs @@ -4,48 +4,47 @@ using IIIF.Serialisation; using Xunit; -namespace IIIF.Tests.Auth.V2 +namespace IIIF.Tests.Auth.V2; + +public class ImageServiceWithAuthTest { - public class ImageServiceWithAuthTest + [Fact] + public void ImageService2_Can_Have_Auth_Services() { - [Fact] - public void ImageService2_Can_Have_Auth_Services() + // Arrange + var imgService2 = new ImageService2 { - // Arrange - var imgService2 = new ImageService2 - { - Id = "https://example.com/image/service", - Service = ReusableParts.Auth2Services - }; - - // Act - var json = imgService2.AsJson().Replace("\r\n", "\n"); - string expected = @"{ + Id = "https://example.com/image/service", + Service = ReusableParts.Auth2Services + }; + + // Act + var json = imgService2.AsJson().Replace("\r\n", "\n"); + var expected = @"{ ""@id"": ""https://example.com/image/service"", ""@type"": ""ImageService2""," + ReusableParts.GetExpectedServiceAsSingle() + "}"; - // Assert - json.Should().BeEquivalentTo(expected); - } - - - [Fact] - public void ImageService3_Can_Have_Auth_Services() + // Assert + json.Should().BeEquivalentTo(expected); + } + + + [Fact] + public void ImageService3_Can_Have_Auth_Services() + { + // Arrange + var imgService3 = new ImageService3 { - // Arrange - var imgService3 = new ImageService3 - { - Id = "https://example.com/image/service", - Service = ReusableParts.Auth2Services - }; - - // Act - var json = imgService3.AsJson().Replace("\r\n", "\n"); - const string expected = @"{ + Id = "https://example.com/image/service", + Service = ReusableParts.Auth2Services + }; + + // Act + var json = imgService3.AsJson().Replace("\r\n", "\n"); + const string expected = @"{ ""id"": ""https://example.com/image/service"", ""type"": ""ImageService3""," + ReusableParts.ExpectedServiceAsArray + @" }"; - // Assert - json.Should().BeEquivalentTo(expected); - } + // Assert + json.Should().BeEquivalentTo(expected); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Auth/V2/ProbeServiceTests.cs b/src/IIIF/IIIF.Tests/Auth/V2/ProbeServiceTests.cs index 7c3472f..6c14efc 100644 --- a/src/IIIF/IIIF.Tests/Auth/V2/ProbeServiceTests.cs +++ b/src/IIIF/IIIF.Tests/Auth/V2/ProbeServiceTests.cs @@ -8,56 +8,55 @@ using IIIF.Serialisation; using Xunit; -namespace IIIF.Tests.Auth.V2 +namespace IIIF.Tests.Auth.V2; + +public class ProbeServiceTests { - public class ProbeServiceTests + [Fact] + public void ProbeService_Serialises() { - [Fact] - public void ProbeService_Serialises() + // Arrange + var probe = new AuthProbeService2 { - // Arrange - var probe = new AuthProbeService2 - { - Id = "https://example.org/resource1/probe", - Label = new LanguageMap("en", "A probe Service"), - ErrorHeading = new LanguageMap("en", "errorHeading value"), - ErrorNote = new LanguageMap("en", "errorNote value") - }; - - // Act - var json = probe.AsJson().Replace("\r\n", "\n"); - const string expected = @"{ + Id = "https://example.org/resource1/probe", + Label = new LanguageMap("en", "A probe Service"), + ErrorHeading = new LanguageMap("en", "errorHeading value"), + ErrorNote = new LanguageMap("en", "errorNote value") + }; + + // Act + var json = probe.AsJson().Replace("\r\n", "\n"); + const string expected = @"{ ""id"": ""https://example.org/resource1/probe"", ""type"": ""AuthProbeService2"", ""label"": {""en"":[""A probe Service""]}, ""errorHeading"": {""en"":[""errorHeading value""]}, ""errorNote"": {""en"":[""errorNote value""]} }"; - - // Assert - json.Should().BeEquivalentTo(expected); - - } - - - [Fact] - public void ProbeServiceResult_Can_Substitute_ImageService2() + + // Assert + json.Should().BeEquivalentTo(expected); + } + + + [Fact] + public void ProbeServiceResult_Can_Substitute_ImageService2() + { + // Arrange + var probe = new AuthProbeResult2 { - // Arrange - var probe = new AuthProbeResult2 + Status = 401, + Substitute = new ImageService2 { - Status = 401, - Substitute = new ImageService2 - { - Id = "https://example.org/imageService2" - }, - Heading = new LanguageMap("en", "heading value"), - Note = new LanguageMap("en", "note value") - }; - - // Act - var json = probe.AsJson().Replace("\r\n", "\n"); - const string expected = @"{ + Id = "https://example.org/imageService2" + }, + Heading = new LanguageMap("en", "heading value"), + Note = new LanguageMap("en", "note value") + }; + + // Act + var json = probe.AsJson().Replace("\r\n", "\n"); + const string expected = @"{ ""@context"": ""http://iiif.io/api/auth/2/context.json"", ""type"": ""AuthProbeResult2"", ""status"": 401, @@ -68,29 +67,29 @@ public void ProbeServiceResult_Can_Substitute_ImageService2() ""heading"": {""en"":[""heading value""]}, ""note"": {""en"":[""note value""]} }"; - - // Assert - json.Should().BeEquivalentTo(expected); - } - - [Fact] - public void ProbeService_Can_Substitute_ImageService3() + + // Assert + json.Should().BeEquivalentTo(expected); + } + + [Fact] + public void ProbeService_Can_Substitute_ImageService3() + { + // Arrange + var probe = new AuthProbeResult2 { - // Arrange - var probe = new AuthProbeResult2 + Status = 401, + Substitute = new ImageService3 { - Status = 401, - Substitute = new ImageService3 - { - Id = "https://example.org/imageService3" - }, - Heading = new LanguageMap("en", "heading value"), - Note = new LanguageMap("en", "note value") - }; - - // Act - var json = probe.AsJson().Replace("\r\n", "\n"); - const string expected = @"{ + Id = "https://example.org/imageService3" + }, + Heading = new LanguageMap("en", "heading value"), + Note = new LanguageMap("en", "note value") + }; + + // Act + var json = probe.AsJson().Replace("\r\n", "\n"); + const string expected = @"{ ""@context"": ""http://iiif.io/api/auth/2/context.json"", ""type"": ""AuthProbeResult2"", ""status"": 401, @@ -101,29 +100,28 @@ public void ProbeService_Can_Substitute_ImageService3() ""heading"": {""en"":[""heading value""]}, ""note"": {""en"":[""note value""]} }"; - - // Assert - json.Should().BeEquivalentTo(expected); - } - - - - [Fact] - public void ProbeService_Can_Provide_Location() + + // Assert + json.Should().BeEquivalentTo(expected); + } + + + [Fact] + public void ProbeService_Can_Provide_Location() + { + // Arrange + var probeResult2 = new AuthProbeResult2 { - // Arrange - var probeResult2 = new AuthProbeResult2 + Status = 200, + Location = new Video { - Status = 200, - Location = new Video - { - Id = "https://example.org/video/12345/file.m3u8" - } - }; - - // Act - var json = probeResult2.AsJson().Replace("\r\n", "\n"); - const string expected = @"{ + Id = "https://example.org/video/12345/file.m3u8" + } + }; + + // Act + var json = probeResult2.AsJson().Replace("\r\n", "\n"); + const string expected = @"{ ""@context"": ""http://iiif.io/api/auth/2/context.json"", ""type"": ""AuthProbeResult2"", ""status"": 200, @@ -132,37 +130,37 @@ public void ProbeService_Can_Provide_Location() ""type"": ""Video"" } }"; - - // Assert - json.Should().BeEquivalentTo(expected); - } + // Assert + json.Should().BeEquivalentTo(expected); + } - [Fact] - public void ProbeService_Can_Provide_AccessService() + + [Fact] + public void ProbeService_Can_Provide_AccessService() + { + // Arrange + var probe = new AuthProbeService2 { - // Arrange - var probe = new AuthProbeService2 + Id = "https://example.org/resource1/probe", + Label = new LanguageMap("en", "A probe service"), + Service = new List() { - Id = "https://example.org/resource1/probe", - Label = new LanguageMap("en", "A probe service"), - Service = new List() + new AuthAccessService2 { - new AuthAccessService2 - { - Id = "https://example.com/auth/access", - Profile = AuthAccessService2.InteractiveProfile, - Label = new LanguageMap("en", "label value"), - Heading = new LanguageMap("en", "heading value"), - Note = new LanguageMap("en", "note value"), - ConfirmLabel = new LanguageMap("en", "confirmLabel value") - } + Id = "https://example.com/auth/access", + Profile = AuthAccessService2.InteractiveProfile, + Label = new LanguageMap("en", "label value"), + Heading = new LanguageMap("en", "heading value"), + Note = new LanguageMap("en", "note value"), + ConfirmLabel = new LanguageMap("en", "confirmLabel value") } - }; - - // Act - var json = probe.AsJson().Replace("\r\n", "\n"); - const string expected = @"{ + } + }; + + // Act + var json = probe.AsJson().Replace("\r\n", "\n"); + const string expected = @"{ ""id"": ""https://example.org/resource1/probe"", ""type"": ""AuthProbeService2"", ""label"": {""en"":[""A probe service""]}, @@ -179,10 +177,7 @@ public void ProbeService_Can_Provide_AccessService() ] }"; - // Assert - json.Should().BeEquivalentTo(expected); - } + // Assert + json.Should().BeEquivalentTo(expected); } - - } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Auth/V2/ReusableParts.cs b/src/IIIF/IIIF.Tests/Auth/V2/ReusableParts.cs index 3410089..7b5cccd 100644 --- a/src/IIIF/IIIF.Tests/Auth/V2/ReusableParts.cs +++ b/src/IIIF/IIIF.Tests/Auth/V2/ReusableParts.cs @@ -2,10 +2,10 @@ using IIIF.Auth.V2; using IIIF.Presentation.V3.Strings; -namespace IIIF.Tests.Auth.V2 +namespace IIIF.Tests.Auth.V2; + +public class ReusableParts { - public class ReusableParts - { public const string ExpectedService = @"{ ""id"": ""https://example.com/image/service/probe"", ""type"": ""AuthProbeService2"", @@ -40,19 +40,19 @@ public class ReusableParts public static string GetExpectedServiceAsSingle() { - return @" + return @" ""service"": " + ExpectedService.Replace("\n ", "\n"); } - public static readonly List Auth2Services = new() + public static readonly List Auth2Services = new() + { + new AuthProbeService2 { - new AuthProbeService2 + Id = "https://example.com/image/service/probe", + Service = new List { - Id = "https://example.com/image/service/probe", - Service = new List + new AuthAccessService2 { - new AuthAccessService2 - { Id = "https://example.com/login", Profile = AuthAccessService2.InteractiveProfile, Label = new LanguageMap("en", "label property"), @@ -61,19 +61,18 @@ public static string GetExpectedServiceAsSingle() Note = new LanguageMap("en", "note property"), Service = new List { - new AuthAccessTokenService2 - { - Id = "https://example.com/token" - }, - new AuthLogoutService2 - { - Id = "https://example.com/logout", - Label = new LanguageMap("en", "Logout from Example Institution") - } + new AuthAccessTokenService2 + { + Id = "https://example.com/token" + }, + new AuthLogoutService2 + { + Id = "https://example.com/logout", + Label = new LanguageMap("en", "Logout from Example Institution") + } } - } } } - }; - } + } + }; } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Auth/V2/TokenServiceTests.cs b/src/IIIF/IIIF.Tests/Auth/V2/TokenServiceTests.cs index 47e49a9..5670fee 100644 --- a/src/IIIF/IIIF.Tests/Auth/V2/TokenServiceTests.cs +++ b/src/IIIF/IIIF.Tests/Auth/V2/TokenServiceTests.cs @@ -4,54 +4,51 @@ using IIIF.Serialisation; using Xunit; -namespace IIIF.Tests.Auth.V2 +namespace IIIF.Tests.Auth.V2; + +public class TokenServiceTests { - public class TokenServiceTests + [Fact] + public void Token_Service_Success_Response() { - [Fact] - public void Token_Service_Success_Response() + // Arrange + var tokenResp = new AuthAccessToken2 { - // Arrange - var tokenResp = new AuthAccessToken2 - { - AccessToken = "TOKEN_HERE", - ExpiresIn = 999, - MessageId = "100" - }; - - // Act - var json = tokenResp.AsJson().Replace("\r\n", "\n"); - var expected = @"{ + AccessToken = "TOKEN_HERE", + ExpiresIn = 999, + MessageId = "100" + }; + + // Act + var json = tokenResp.AsJson().Replace("\r\n", "\n"); + var expected = @"{ ""@context"": ""http://iiif.io/api/auth/2/context.json"", ""type"": ""AuthAccessToken2"", ""messageId"": ""100"", ""accessToken"": ""TOKEN_HERE"", ""expiresIn"": 999 }"; - // Assert - json.Should().BeEquivalentTo(expected); + // Assert + json.Should().BeEquivalentTo(expected); + } - } - - [Fact] - public void Token_Service_Error_Response() - { - // Arrange - var tokenResp = new AuthAccessTokenError2( - AuthAccessTokenError2.InvalidAspect, - new LanguageMap("en", "Your credentials are wrong")); - - // Act - var json = tokenResp.AsJson().Replace("\r\n", "\n"); - var expected = @"{ + [Fact] + public void Token_Service_Error_Response() + { + // Arrange + var tokenResp = new AuthAccessTokenError2( + AuthAccessTokenError2.InvalidAspect, + new LanguageMap("en", "Your credentials are wrong")); + + // Act + var json = tokenResp.AsJson().Replace("\r\n", "\n"); + var expected = @"{ ""@context"": ""http://iiif.io/api/auth/2/context.json"", ""type"": ""AuthAccessTokenError2"", ""profile"": ""invalidAspect"", ""note"": {""en"":[""Your credentials are wrong""]} }"; - // Assert - json.Should().BeEquivalentTo(expected); - - } + // Assert + json.Should().BeEquivalentTo(expected); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/IIIF.Tests.csproj b/src/IIIF/IIIF.Tests/IIIF.Tests.csproj index 555910f..30b81a3 100644 --- a/src/IIIF/IIIF.Tests/IIIF.Tests.csproj +++ b/src/IIIF/IIIF.Tests/IIIF.Tests.csproj @@ -6,9 +6,9 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -20,7 +20,7 @@ - + diff --git a/src/IIIF/IIIF.Tests/ImageApi/ImageRequestXTests.cs b/src/IIIF/IIIF.Tests/ImageApi/ImageRequestXTests.cs index b33653e..3b2f183 100644 --- a/src/IIIF/IIIF.Tests/ImageApi/ImageRequestXTests.cs +++ b/src/IIIF/IIIF.Tests/ImageApi/ImageRequestXTests.cs @@ -2,224 +2,223 @@ using IIIF.ImageApi; using Xunit; -namespace IIIF.Tests.ImageApi +namespace IIIF.Tests.ImageApi; + +public class ImageRequestXTests { - public class ImageRequestXTests + [Fact] + public void Parse_CorrectMax() { - [Fact] - public void Parse_CorrectMax() + // Arrange + var sizeParameter = new SizeParameter { - // Arrange - var sizeParameter = new SizeParameter - { - Max = true - }; - var originalSize = new Size(400, 400); - var expected = new Size(400, 400); - - // Act - var result = sizeParameter.GetResultingSize(originalSize); - - // Assert - result.Should().BeEquivalentTo(expected); - } - - [Fact] - public void Parse_CorrectMaxScaled() + Max = true + }; + var originalSize = new Size(400, 400); + var expected = new Size(400, 400); + + // Act + var result = sizeParameter.GetResultingSize(originalSize); + + // Assert + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public void Parse_CorrectMaxScaled() + { + // Arrange + var sizeParameter = new SizeParameter { - // Arrange - var sizeParameter = new SizeParameter - { - Max = true, Upscaled = true - }; - var originalSize = new Size(400, 400); - var expected = new Size(400, 400); - - // Act - var result = sizeParameter.GetResultingSize(originalSize); - - // Assert - result.Should().BeEquivalentTo(expected); - } - - [Fact] - public void Parse_CorrectWidthOnly() + Max = true, Upscaled = true + }; + var originalSize = new Size(400, 400); + var expected = new Size(400, 400); + + // Act + var result = sizeParameter.GetResultingSize(originalSize); + + // Assert + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public void Parse_CorrectWidthOnly() + { + // Arrange + var sizeParameter = new SizeParameter { - // Arrange - var sizeParameter = new SizeParameter - { - Width = 100 - }; - var originalSize = new Size(400, 800); - var expected = new Size(100, 200); - - // Act - var result = sizeParameter.GetResultingSize(originalSize); - - // Assert - result.Should().BeEquivalentTo(expected); - } - - [Fact] - public void Parse_CorrectWidthOnlyScaled() + Width = 100 + }; + var originalSize = new Size(400, 800); + var expected = new Size(100, 200); + + // Act + var result = sizeParameter.GetResultingSize(originalSize); + + // Assert + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public void Parse_CorrectWidthOnlyScaled() + { + // Arrange + var sizeParameter = new SizeParameter { - // Arrange - var sizeParameter = new SizeParameter - { - Width = 100, Upscaled = true - }; - var originalSize = new Size(400, 800); - var expected = new Size(100, 200); - - // Act - var result = sizeParameter.GetResultingSize(originalSize); - - // Assert - result.Should().BeEquivalentTo(expected); - } - - [Fact] - public void Parse_CorrectHeightOnly() + Width = 100, Upscaled = true + }; + var originalSize = new Size(400, 800); + var expected = new Size(100, 200); + + // Act + var result = sizeParameter.GetResultingSize(originalSize); + + // Assert + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public void Parse_CorrectHeightOnly() + { + // Arrange + var sizeParameter = new SizeParameter { - // Arrange - var sizeParameter = new SizeParameter - { - Height = 40 - }; - var originalSize = new Size(400, 800); - var expected = new Size(20, 40); - - // Act - var result = sizeParameter.GetResultingSize(originalSize); - - // Assert - result.Should().BeEquivalentTo(expected); - } - - [Fact] - public void Parse_CorrectHeightOnlyScaled() + Height = 40 + }; + var originalSize = new Size(400, 800); + var expected = new Size(20, 40); + + // Act + var result = sizeParameter.GetResultingSize(originalSize); + + // Assert + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public void Parse_CorrectHeightOnlyScaled() + { + // Arrange + var sizeParameter = new SizeParameter { - // Arrange - var sizeParameter = new SizeParameter - { - Height = 40, Upscaled = true - }; - var originalSize = new Size(400, 800); - var expected = new Size(20, 40); - - // Act - var result = sizeParameter.GetResultingSize(originalSize); - - // Assert - result.Should().BeEquivalentTo(expected); - } - - [Fact] - public void Parse_CorrectWidthHeight() + Height = 40, Upscaled = true + }; + var originalSize = new Size(400, 800); + var expected = new Size(20, 40); + + // Act + var result = sizeParameter.GetResultingSize(originalSize); + + // Assert + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public void Parse_CorrectWidthHeight() + { + // Arrange + var sizeParameter = new SizeParameter { - // Arrange - var sizeParameter = new SizeParameter - { - Width = 90, Height = 40 - }; - var originalSize = new Size(400, 800); - var expected = new Size(90, 40); - - // Act - var result = sizeParameter.GetResultingSize(originalSize); - - // Assert - result.Should().BeEquivalentTo(expected); - } - - [Fact] - public void Parse_CorrectWidthHeightScaled() + Width = 90, Height = 40 + }; + var originalSize = new Size(400, 800); + var expected = new Size(90, 40); + + // Act + var result = sizeParameter.GetResultingSize(originalSize); + + // Assert + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public void Parse_CorrectWidthHeightScaled() + { + // Arrange + var sizeParameter = new SizeParameter { - // Arrange - var sizeParameter = new SizeParameter - { - Width = 90, Height = 40, Upscaled = true - }; - var originalSize = new Size(400, 800); - var expected = new Size(90, 40); - - // Act - var result = sizeParameter.GetResultingSize(originalSize); - - // Assert - result.Should().BeEquivalentTo(expected); - } - - [Fact] - public void Parse_CorrectWidthHeightConfined() + Width = 90, Height = 40, Upscaled = true + }; + var originalSize = new Size(400, 800); + var expected = new Size(90, 40); + + // Act + var result = sizeParameter.GetResultingSize(originalSize); + + // Assert + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public void Parse_CorrectWidthHeightConfined() + { + // Arrange + var sizeParameter = new SizeParameter { - // Arrange - var sizeParameter = new SizeParameter - { - Width = 90, Height = 40, Confined = true - }; - var originalSize = new Size(400, 800); - var expected = new Size(20, 40); - - // Act - var result = sizeParameter.GetResultingSize(originalSize); - - // Assert - result.Should().BeEquivalentTo(expected); - } - - [Fact] - public void Parse_CorrectWidthHeightScaledConfined() + Width = 90, Height = 40, Confined = true + }; + var originalSize = new Size(400, 800); + var expected = new Size(20, 40); + + // Act + var result = sizeParameter.GetResultingSize(originalSize); + + // Assert + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public void Parse_CorrectWidthHeightScaledConfined() + { + // Arrange + var sizeParameter = new SizeParameter { - // Arrange - var sizeParameter = new SizeParameter - { - Width = 90, Height = 40, Confined = true, Upscaled = true - }; - var originalSize = new Size(400, 800); - var expected = new Size(20, 40); - - // Act - var result = sizeParameter.GetResultingSize(originalSize); - - // Assert - result.Should().BeEquivalentTo(expected); - } - - [Fact] - public void Parse_CorrectPercentage() + Width = 90, Height = 40, Confined = true, Upscaled = true + }; + var originalSize = new Size(400, 800); + var expected = new Size(20, 40); + + // Act + var result = sizeParameter.GetResultingSize(originalSize); + + // Assert + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public void Parse_CorrectPercentage() + { + // Arrange + var sizeParameter = new SizeParameter { - // Arrange - var sizeParameter = new SizeParameter - { - PercentScale = 30f - }; - var originalSize = new Size(400, 800); - var expected = new Size(120, 240); - - // Act - var result = sizeParameter.GetResultingSize(originalSize); - - // Assert - result.Should().BeEquivalentTo(expected); - } - - [Fact] - public void Parse_CorrectPercentageScaled() + PercentScale = 30f + }; + var originalSize = new Size(400, 800); + var expected = new Size(120, 240); + + // Act + var result = sizeParameter.GetResultingSize(originalSize); + + // Assert + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public void Parse_CorrectPercentageScaled() + { + // Arrange + var sizeParameter = new SizeParameter { - // Arrange - var sizeParameter = new SizeParameter - { - PercentScale = 30f, Upscaled = true - }; - var originalSize = new Size(400, 800); - var expected = new Size(120, 240); - - // Act - var result = sizeParameter.GetResultingSize(originalSize); - - // Assert - result.Should().BeEquivalentTo(expected); - } + PercentScale = 30f, Upscaled = true + }; + var originalSize = new Size(400, 800); + var expected = new Size(120, 240); + + // Act + var result = sizeParameter.GetResultingSize(originalSize); + + // Assert + result.Should().BeEquivalentTo(expected); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/ImageApi/SizeParameterTests.cs b/src/IIIF/IIIF.Tests/ImageApi/SizeParameterTests.cs index df5835e..ff036fc 100644 --- a/src/IIIF/IIIF.Tests/ImageApi/SizeParameterTests.cs +++ b/src/IIIF/IIIF.Tests/ImageApi/SizeParameterTests.cs @@ -2,224 +2,223 @@ using IIIF.ImageApi; using Xunit; -namespace IIIF.Tests.ImageApi +namespace IIIF.Tests.ImageApi; + +public class SizeParameterTests { - public class SizeParameterTests + [Fact] + public void Parse_CorrectMax() { - [Fact] - public void Parse_CorrectMax() + // Arrange + const string size = "max"; + var expected = new SizeParameter { - // Arrange - const string size = "max"; - var expected = new SizeParameter - { - Max = true - }; - - // Act - var sizeParameter = SizeParameter.Parse(size); - - // Assert - sizeParameter.Should().BeEquivalentTo(expected); - sizeParameter.ToString().Should().Be(size); - } - - [Fact] - public void Parse_CorrectMaxScaled() + Max = true + }; + + // Act + var sizeParameter = SizeParameter.Parse(size); + + // Assert + sizeParameter.Should().BeEquivalentTo(expected); + sizeParameter.ToString().Should().Be(size); + } + + [Fact] + public void Parse_CorrectMaxScaled() + { + // Arrange + const string size = "^max"; + var expected = new SizeParameter { - // Arrange - const string size = "^max"; - var expected = new SizeParameter - { - Max = true, Upscaled = true - }; - - // Act - var sizeParameter = SizeParameter.Parse(size); - - // Assert - sizeParameter.Should().BeEquivalentTo(expected); - sizeParameter.ToString().Should().Be(size); - } - - [Fact] - public void Parse_CorrectWidthOnly() + Max = true, Upscaled = true + }; + + // Act + var sizeParameter = SizeParameter.Parse(size); + + // Assert + sizeParameter.Should().BeEquivalentTo(expected); + sizeParameter.ToString().Should().Be(size); + } + + [Fact] + public void Parse_CorrectWidthOnly() + { + // Arrange + const string size = "100,"; + var expected = new SizeParameter { - // Arrange - const string size = "100,"; - var expected = new SizeParameter - { - Width = 100 - }; - - // Act - var sizeParameter = SizeParameter.Parse(size); - - // Assert - sizeParameter.Should().BeEquivalentTo(expected); - sizeParameter.ToString().Should().Be(size); - } - - [Fact] - public void Parse_CorrectWidthOnlyScaled() + Width = 100 + }; + + // Act + var sizeParameter = SizeParameter.Parse(size); + + // Assert + sizeParameter.Should().BeEquivalentTo(expected); + sizeParameter.ToString().Should().Be(size); + } + + [Fact] + public void Parse_CorrectWidthOnlyScaled() + { + // Arrange + const string size = "^100,"; + var expected = new SizeParameter { - // Arrange - const string size = "^100,"; - var expected = new SizeParameter - { - Width = 100, Upscaled = true - }; - - // Act - var sizeParameter = SizeParameter.Parse(size); - - // Assert - sizeParameter.Should().BeEquivalentTo(expected); - sizeParameter.ToString().Should().Be(size); - } - - [Fact] - public void Parse_CorrectHeightOnly() + Width = 100, Upscaled = true + }; + + // Act + var sizeParameter = SizeParameter.Parse(size); + + // Assert + sizeParameter.Should().BeEquivalentTo(expected); + sizeParameter.ToString().Should().Be(size); + } + + [Fact] + public void Parse_CorrectHeightOnly() + { + // Arrange + const string size = ",40"; + var expected = new SizeParameter { - // Arrange - const string size = ",40"; - var expected = new SizeParameter - { - Height = 40 - }; - - // Act - var sizeParameter = SizeParameter.Parse(size); - - // Assert - sizeParameter.Should().BeEquivalentTo(expected); - sizeParameter.ToString().Should().Be(size); - } - - [Fact] - public void Parse_CorrectHeightOnlyScaled() + Height = 40 + }; + + // Act + var sizeParameter = SizeParameter.Parse(size); + + // Assert + sizeParameter.Should().BeEquivalentTo(expected); + sizeParameter.ToString().Should().Be(size); + } + + [Fact] + public void Parse_CorrectHeightOnlyScaled() + { + // Arrange + const string size = "^,40"; + var expected = new SizeParameter { - // Arrange - const string size = "^,40"; - var expected = new SizeParameter - { - Height = 40, Upscaled = true - }; - - // Act - var sizeParameter = SizeParameter.Parse(size); - - // Assert - sizeParameter.Should().BeEquivalentTo(expected); - sizeParameter.ToString().Should().Be(size); - } - - [Fact] - public void Parse_CorrectWidthHeight() + Height = 40, Upscaled = true + }; + + // Act + var sizeParameter = SizeParameter.Parse(size); + + // Assert + sizeParameter.Should().BeEquivalentTo(expected); + sizeParameter.ToString().Should().Be(size); + } + + [Fact] + public void Parse_CorrectWidthHeight() + { + // Arrange + const string size = "90,40"; + var expected = new SizeParameter { - // Arrange - const string size = "90,40"; - var expected = new SizeParameter - { - Width = 90, Height = 40 - }; - - // Act - var sizeParameter = SizeParameter.Parse(size); - - // Assert - sizeParameter.Should().BeEquivalentTo(expected); - sizeParameter.ToString().Should().Be(size); - } - - [Fact] - public void Parse_CorrectWidthHeightScaled() + Width = 90, Height = 40 + }; + + // Act + var sizeParameter = SizeParameter.Parse(size); + + // Assert + sizeParameter.Should().BeEquivalentTo(expected); + sizeParameter.ToString().Should().Be(size); + } + + [Fact] + public void Parse_CorrectWidthHeightScaled() + { + // Arrange + const string size = "^90,40"; + var expected = new SizeParameter { - // Arrange - const string size = "^90,40"; - var expected = new SizeParameter - { - Width = 90, Height = 40, Upscaled = true - }; - - // Act - var sizeParameter = SizeParameter.Parse(size); - - // Assert - sizeParameter.Should().BeEquivalentTo(expected); - sizeParameter.ToString().Should().Be(size); - } - - [Fact] - public void Parse_CorrectWidthHeightConfined() + Width = 90, Height = 40, Upscaled = true + }; + + // Act + var sizeParameter = SizeParameter.Parse(size); + + // Assert + sizeParameter.Should().BeEquivalentTo(expected); + sizeParameter.ToString().Should().Be(size); + } + + [Fact] + public void Parse_CorrectWidthHeightConfined() + { + // Arrange + const string size = "!90,40"; + var expected = new SizeParameter { - // Arrange - const string size = "!90,40"; - var expected = new SizeParameter - { - Width = 90, Height = 40, Confined = true - }; - - // Act - var sizeParameter = SizeParameter.Parse(size); - - // Assert - sizeParameter.Should().BeEquivalentTo(expected); - sizeParameter.ToString().Should().Be(size); - } - - [Fact] - public void Parse_CorrectWidthHeightScaledConfined() + Width = 90, Height = 40, Confined = true + }; + + // Act + var sizeParameter = SizeParameter.Parse(size); + + // Assert + sizeParameter.Should().BeEquivalentTo(expected); + sizeParameter.ToString().Should().Be(size); + } + + [Fact] + public void Parse_CorrectWidthHeightScaledConfined() + { + // Arrange + const string size = "^!90,40"; + var expected = new SizeParameter { - // Arrange - const string size = "^!90,40"; - var expected = new SizeParameter - { - Width = 90, Height = 40, Confined = true, Upscaled = true - }; - - // Act - var sizeParameter = SizeParameter.Parse(size); - - // Assert - sizeParameter.Should().BeEquivalentTo(expected); - sizeParameter.ToString().Should().Be(size); - } - - [Fact] - public void Parse_CorrectPercentage() + Width = 90, Height = 40, Confined = true, Upscaled = true + }; + + // Act + var sizeParameter = SizeParameter.Parse(size); + + // Assert + sizeParameter.Should().BeEquivalentTo(expected); + sizeParameter.ToString().Should().Be(size); + } + + [Fact] + public void Parse_CorrectPercentage() + { + // Arrange + const string size = "pct:30"; + var expected = new SizeParameter { - // Arrange - const string size = "pct:30"; - var expected = new SizeParameter - { - PercentScale = 30f - }; - - // Act - var sizeParameter = SizeParameter.Parse(size); - - // Assert - sizeParameter.Should().BeEquivalentTo(expected); - sizeParameter.ToString().Should().Be(size); - } - - [Fact] - public void Parse_CorrectPercentageScaled() + PercentScale = 30f + }; + + // Act + var sizeParameter = SizeParameter.Parse(size); + + // Assert + sizeParameter.Should().BeEquivalentTo(expected); + sizeParameter.ToString().Should().Be(size); + } + + [Fact] + public void Parse_CorrectPercentageScaled() + { + // Arrange + const string size = "^pct:30"; + var expected = new SizeParameter { - // Arrange - const string size = "^pct:30"; - var expected = new SizeParameter - { - PercentScale = 30f, Upscaled = true - }; - - // Act - var sizeParameter = SizeParameter.Parse(size); - - // Assert - sizeParameter.Should().BeEquivalentTo(expected); - sizeParameter.ToString().Should().Be(size); - } + PercentScale = 30f, Upscaled = true + }; + + // Act + var sizeParameter = SizeParameter.Parse(size); + + // Assert + sizeParameter.Should().BeEquivalentTo(expected); + sizeParameter.ToString().Should().Be(size); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/ImageApi/SizeTests.cs b/src/IIIF/IIIF.Tests/ImageApi/SizeTests.cs index ae33cea..612d928 100644 --- a/src/IIIF/IIIF.Tests/ImageApi/SizeTests.cs +++ b/src/IIIF/IIIF.Tests/ImageApi/SizeTests.cs @@ -4,412 +4,418 @@ using FluentAssertions; using Xunit; -namespace IIIF.Tests.ImageApi +namespace IIIF.Tests.ImageApi; + +public class SizeTests { - public class SizeTests + [Fact] + public void FromArray_ReturnsCorrectSize() { - [Fact] - public void FromArray_ReturnsCorrectSize() - { - // Arrange - const int width = 1234; - const int height = 8876; - var widthHeight = new[] {width, height}; - - // Act - var size = Size.FromArray(widthHeight); - - // Assert - size.Width.Should().Be(width); - size.Height.Should().Be(height); - } + // Arrange + const int width = 1234; + const int height = 8876; + var widthHeight = new[] { width, height }; - [Fact] - public void FromString_ReturnsCorrect_WH() - { - // Arrange - const string actual = "100,200"; - - // Act - var size = Size.FromString(actual); - - // Assert - size.Width.Should().Be(100); - size.Height.Should().Be(200); - } + // Act + var size = Size.FromArray(widthHeight); - [Fact] - public void ToArray_ReturnsWidthHeight() - { - // Arrange - const int width = 1234; - const int height = 8876; - var expected = new[] {width, height}; - var size = new Size(width, height); - - // Act - var result = size.ToArray(); - - // Assert - result.Should().BeEquivalentTo(expected); - } - - [Fact] - public void ToString_ReturnsWidthHeight() - { - // Arrange - const int width = 1234; - const int height = 8876; - var expected = $"{width},{height}"; - var size = new Size(width, height); - - // Act - var result = size.ToString(); - - // Assert - result.Should().Be(expected); - } - - [Theory, MemberData(nameof(ConfineDataSquare))] - public void ConfineStaticMethod_BoundingSize_ReturnsCorrectSize(TestSizeData testData) - { - // Arrange - var size = new Size(testData.CurrentWidth, testData.CurrentHeight); - - // Act - var confined = Size.Confine(testData.ConfineWidth, size); - - // Assert - confined.Width.Should().Be(testData.ExpectedWidth); - confined.Height.Should().Be(testData.ExpectedHeight); - } - - [Theory, MemberData(nameof(ConfineData))] - public void ConfineStaticMethod_ReturnsCorrectSize(TestSizeData testData) - { - // Arrange - var size = new Size(testData.CurrentWidth, testData.CurrentHeight); - var requiredSize = new Size(testData.ConfineWidth, testData.ConfineHeight); - - // Act - var confined = Size.Confine(requiredSize, size); - - // Assert - confined.Width.Should().Be(testData.ExpectedWidth); - confined.Height.Should().Be(testData.ExpectedHeight); - } + // Assert + size.Width.Should().Be(width); + size.Height.Should().Be(height); + } - [Fact] - public void Resize_Throws_IfHeightAndWidthNull() - { - // Arrange - var size = new Size(100, 100); - - // Act - Action action = () => Size.Resize(size, null, null); - - // Assert - action.Should().Throw(); - } - - [Fact] - public void Resize_ReturnsCorrectSize_IfHeightAndWidthSpecified() - { - // Arrange - var size = new Size(100, 100); - - // Act - var newSize = Size.Resize(size, 90, 400); - - // Assert - newSize.Width.Should().Be(90); - newSize.Height.Should().Be(400); - } - - [Theory] - [InlineData(400, null)] - [InlineData(null, 400)] - public void Resize_ReturnsNewSize_Square(int? width, int? height) - { - // Arrange - var size = new Size(100, 100); - - // Act - var newSize = Size.Resize(size, width, height); - - // Assert - newSize.Width.Should().Be(width ?? height); - newSize.Height.Should().Be(width ?? height); - } + [Fact] + public void FromString_ReturnsCorrect_WH() + { + // Arrange + const string actual = "100,200"; - [Theory] - [InlineData(400, 200)] - [InlineData(50, 25)] - public void Resize_ReturnsCorrectSize_Landscape_FromWidth(int width, int expectedHeight) - { - // Arrange - var size = new Size(200, 100); - - // Act - var newSize = Size.Resize(size, width); - - // Assert - newSize.Width.Should().Be(width); - newSize.Height.Should().Be(expectedHeight); - } - - [Theory] - [InlineData(200, 400)] - [InlineData(25, 50)] - public void Resize_ReturnsCorrectSize_Landscape_FromHeight(int height, int expectedWidth) - { - // Arrange - var size = new Size(200, 100); - - // Act - var newSize = Size.Resize(size, targetHeight: height); - - // Assert - newSize.Height.Should().Be(height); - newSize.Width.Should().Be(expectedWidth); - } + // Act + var size = Size.FromString(actual); - [Theory] - [InlineData(200, 400)] - [InlineData(25, 50)] - public void Resize_ReturnsCorrectSize_Portrait_FromWidth(int width, int expectedHeight) - { - // Arrange - var size = new Size(100, 200); - - // Act - var newSize = Size.Resize(size, width); - - // Assert - newSize.Width.Should().Be(width); - newSize.Height.Should().Be(expectedHeight); - } - - [Theory] - [InlineData(400, 200)] - [InlineData(50, 25)] - public void Resize_ReturnsCorrectSize_Portrait_FromHeight(int height, int expectedWidth) - { - // Arrange - var size = new Size(100, 200); - - // Act - var newSize = Size.Resize(size, targetHeight: height); - - // Assert - newSize.Height.Should().Be(height); - newSize.Width.Should().Be(expectedWidth); - } + // Assert + size.Width.Should().Be(100); + size.Height.Should().Be(200); + } - [Fact] - public void Resize_Percentage_CorrectSmaller() - { - // Arrange - var size = new Size(100, 200); - - // Act - var newSize = Size.ResizePercent(size, 30f); - - // Assert - newSize.Width.Should().Be(30); - newSize.Height.Should().Be(60); - } - - [Fact] - public void Resize_Percentage_CorrectLarger() - { - // Arrange - var size = new Size(100, 200); - - // Act - var newSize = Size.ResizePercent(size, 130f); - - // Assert - newSize.Width.Should().Be(130); - newSize.Height.Should().Be(260); - } + [Fact] + public void ToArray_ReturnsWidthHeight() + { + // Arrange + const int width = 1234; + const int height = 8876; + var expected = new[] { width, height }; + var size = new Size(width, height); - [Theory] - [InlineData(100, 100, 50, 10, 10, 10)] // shrink square to landscape - [InlineData(100, 100, 10, 50, 10, 10)] // shrink square to portrait - [InlineData(100, 100, 50, 50, 50, 50)] // shrink square to square - [InlineData(100, 100, 500, 600, 500, 500)] // grow square to landscape - [InlineData(100, 100, 500, 300, 300, 300)] // grow square to portrait - [InlineData(100, 100, 500, 500, 500, 500)] // grow square to square - [InlineData(200, 100, 50, 10, 20, 10)] // shrink landscape to landscape - [InlineData(200, 100, 10, 50, 10, 5)] // shrink landscape to portrait - [InlineData(200, 100, 50, 50, 50, 25)] // shrink landscape to square - [InlineData(200, 100, 500, 300, 500, 250)] // grow landscape to landscape - [InlineData(200, 100, 500, 600, 500, 250)] // grow landscape to portrait - [InlineData(200, 100, 500, 500, 500, 250)] // grow landscape to square - [InlineData(100, 200, 50, 10, 5, 10)] // shrink portrait to landscape - [InlineData(100, 200, 10, 50, 10, 20)] // shrink portrait to portrait - [InlineData(100, 200, 50, 50, 25, 50)] // shrink portrait to square - [InlineData(100, 200, 500, 300, 150, 300)] // grow portrait to landscape - [InlineData(100, 200, 500, 600, 300, 600)] // grow portrait to portrait - [InlineData(100, 200, 500, 500, 250, 500)] // grow portrait to square - public void FitWithin_Returns_CorrectSize(int w, int h, int targetWidth, int targetHeight, int expectedWidth, int expectedHeight) - { - // Arrange - var size = new Size(w, h); - - // Act - var newSize = Size.FitWithin(new Size(targetWidth, targetHeight), size); - - // Assert - newSize.Height.Should().Be(expectedHeight); - newSize.Width.Should().Be(expectedWidth); - } - - [Theory] - [InlineData(10, 10, 10, 10)] // square same size - [InlineData(10, 5, 10, 5)] // landscape same size - [InlineData(5, 10, 5, 10)] // portrait same size - [InlineData(10, 5, 10, 10)] // landscape within - [InlineData(5, 10, 10, 10)] // portrait within - [InlineData(5, 5, 10, 10)] // both dimensions within - public void IsConfinedWithin_True_IfConfinedWithin(int width, int height, int confinedWidth, int confinedHeight) - { - // Arrange - var size = new Size(width, height); - var confineSize = new Size(confinedWidth, confinedHeight); + // Act + var result = size.ToArray(); - // Assert - size.IsConfinedWithin(confineSize).Should().BeTrue(); - } - - [Theory] - [InlineData(11, 11, 10, 10)] // both dimensions out - [InlineData(11, 9, 10, 10)] // width outside, height inside - [InlineData(9, 11, 10, 10)] // height outside, width inside - public void IsConfinedWithin_False_IfNotConfinedWithin(int width, int height, int confinedWidth, int confinedHeight) - { - // Arrange - var size = new Size(width, height); - var confineSize = new Size(confinedWidth, confinedHeight); + // Assert + result.Should().BeEquivalentTo(expected); + } - // Assert - size.IsConfinedWithin(confineSize).Should().BeFalse(); - } + [Fact] + public void ToString_ReturnsWidthHeight() + { + // Arrange + const int width = 1234; + const int height = 8876; + var expected = $"{width},{height}"; + var size = new Size(width, height); - [Theory] - [InlineData(10, 10, 10)] - [InlineData(5, 10, 10)] - [InlineData(10, 5, 10)] - public void MaxDimension_Correct(int w, int h, int expected) - => new Size(w, h).MaxDimension.Should().Be(expected); - - [Theory] - [InlineData(10, 10, ImageShape.Square)] - [InlineData(5, 10, ImageShape.Portrait)] - [InlineData(10, 5, ImageShape.Landscape)] - public void GetShape_Correct(int w, int h, ImageShape expected) - => new Size(w, h).GetShape().Should().Be(expected); - - [Fact] - public void GetSizeIncreasePercent_Correct_SameSize() - { - // Arrange - var large = new Size(100, 200); - var small = new Size(100, 200); - - // Act - var difference = Size.GetSizeIncreasePercent(large, small); - - // Assert - difference.Should().Be(0); - } - - [Fact] - public void GetSizeIncreasePercent_Correct() - { - // Arrange - var large = new Size(300, 400); - var small = new Size(100, 200); - - // Act - var difference = Size.GetSizeIncreasePercent(large, small); - - // Assert - difference.Should().Be(100); - } - - [Fact] - public void GetSizeIncreasePercent_Throws_IfSmallerLarger() - { - // Arrange - var large = new Size(300, 400); - var small = new Size(100, 800); - - // Act - Action action = () => Size.GetSizeIncreasePercent(large, small); - - // Assert - action.Should().Throw(); - } + // Act + var result = size.ToString(); - private static List sampleTestData = new() - { - // currW, currH, confW, confH, expectedW, expectedH - new(200, 200, 300, 300, 200, 200), // actual smaller than confine - new(500, 500, 300, 300, 300, 300), // confined square - new(500, 400, 300, 300, 300, 240), // current portrait - new(400, 500, 300, 300, 240, 300), // current landscape - new(500, 500, 300, 200, 200, 200), // target portrait - new(500, 500, 200, 300, 200, 300), // target landscape - new(4553, 5668, 200, 200, 161, 200), // a specific rounding issue with Appetiser - }; - - // Test data for square confine targets only - public static IEnumerable ConfineDataSquare + // Assert + result.Should().Be(expected); + } + + [Theory] + [MemberData(nameof(ConfineDataSquare))] + public void ConfineStaticMethod_BoundingSize_ReturnsCorrectSize(TestSizeData testData) + { + // Arrange + var size = new Size(testData.CurrentWidth, testData.CurrentHeight); + + // Act + var confined = Size.Confine(testData.ConfineWidth, size); + + // Assert + confined.Width.Should().Be(testData.ExpectedWidth); + confined.Height.Should().Be(testData.ExpectedHeight); + } + + [Theory] + [MemberData(nameof(ConfineData))] + public void ConfineStaticMethod_ReturnsCorrectSize(TestSizeData testData) + { + // Arrange + var size = new Size(testData.CurrentWidth, testData.CurrentHeight); + var requiredSize = new Size(testData.ConfineWidth, testData.ConfineHeight); + + // Act + var confined = Size.Confine(requiredSize, size); + + // Assert + confined.Width.Should().Be(testData.ExpectedWidth); + confined.Height.Should().Be(testData.ExpectedHeight); + } + + [Fact] + public void Resize_Throws_IfHeightAndWidthNull() + { + // Arrange + var size = new Size(100, 100); + + // Act + Action action = () => Size.Resize(size, null, null); + + // Assert + action.Should().Throw(); + } + + [Fact] + public void Resize_ReturnsCorrectSize_IfHeightAndWidthSpecified() + { + // Arrange + var size = new Size(100, 100); + + // Act + var newSize = Size.Resize(size, 90, 400); + + // Assert + newSize.Width.Should().Be(90); + newSize.Height.Should().Be(400); + } + + [Theory] + [InlineData(400, null)] + [InlineData(null, 400)] + public void Resize_ReturnsNewSize_Square(int? width, int? height) + { + // Arrange + var size = new Size(100, 100); + + // Act + var newSize = Size.Resize(size, width, height); + + // Assert + newSize.Width.Should().Be(width ?? height); + newSize.Height.Should().Be(width ?? height); + } + + [Theory] + [InlineData(400, 200)] + [InlineData(50, 25)] + public void Resize_ReturnsCorrectSize_Landscape_FromWidth(int width, int expectedHeight) + { + // Arrange + var size = new Size(200, 100); + + // Act + var newSize = Size.Resize(size, width); + + // Assert + newSize.Width.Should().Be(width); + newSize.Height.Should().Be(expectedHeight); + } + + [Theory] + [InlineData(200, 400)] + [InlineData(25, 50)] + public void Resize_ReturnsCorrectSize_Landscape_FromHeight(int height, int expectedWidth) + { + // Arrange + var size = new Size(200, 100); + + // Act + var newSize = Size.Resize(size, targetHeight: height); + + // Assert + newSize.Height.Should().Be(height); + newSize.Width.Should().Be(expectedWidth); + } + + [Theory] + [InlineData(200, 400)] + [InlineData(25, 50)] + public void Resize_ReturnsCorrectSize_Portrait_FromWidth(int width, int expectedHeight) + { + // Arrange + var size = new Size(100, 200); + + // Act + var newSize = Size.Resize(size, width); + + // Assert + newSize.Width.Should().Be(width); + newSize.Height.Should().Be(expectedHeight); + } + + [Theory] + [InlineData(400, 200)] + [InlineData(50, 25)] + public void Resize_ReturnsCorrectSize_Portrait_FromHeight(int height, int expectedWidth) + { + // Arrange + var size = new Size(100, 200); + + // Act + var newSize = Size.Resize(size, targetHeight: height); + + // Assert + newSize.Height.Should().Be(height); + newSize.Width.Should().Be(expectedWidth); + } + + [Fact] + public void Resize_Percentage_CorrectSmaller() + { + // Arrange + var size = new Size(100, 200); + + // Act + var newSize = Size.ResizePercent(size, 30f); + + // Assert + newSize.Width.Should().Be(30); + newSize.Height.Should().Be(60); + } + + [Fact] + public void Resize_Percentage_CorrectLarger() + { + // Arrange + var size = new Size(100, 200); + + // Act + var newSize = Size.ResizePercent(size, 130f); + + // Assert + newSize.Width.Should().Be(130); + newSize.Height.Should().Be(260); + } + + [Theory] + [InlineData(100, 100, 50, 10, 10, 10)] // shrink square to landscape + [InlineData(100, 100, 10, 50, 10, 10)] // shrink square to portrait + [InlineData(100, 100, 50, 50, 50, 50)] // shrink square to square + [InlineData(100, 100, 500, 600, 500, 500)] // grow square to landscape + [InlineData(100, 100, 500, 300, 300, 300)] // grow square to portrait + [InlineData(100, 100, 500, 500, 500, 500)] // grow square to square + [InlineData(200, 100, 50, 10, 20, 10)] // shrink landscape to landscape + [InlineData(200, 100, 10, 50, 10, 5)] // shrink landscape to portrait + [InlineData(200, 100, 50, 50, 50, 25)] // shrink landscape to square + [InlineData(200, 100, 500, 300, 500, 250)] // grow landscape to landscape + [InlineData(200, 100, 500, 600, 500, 250)] // grow landscape to portrait + [InlineData(200, 100, 500, 500, 500, 250)] // grow landscape to square + [InlineData(100, 200, 50, 10, 5, 10)] // shrink portrait to landscape + [InlineData(100, 200, 10, 50, 10, 20)] // shrink portrait to portrait + [InlineData(100, 200, 50, 50, 25, 50)] // shrink portrait to square + [InlineData(100, 200, 500, 300, 150, 300)] // grow portrait to landscape + [InlineData(100, 200, 500, 600, 300, 600)] // grow portrait to portrait + [InlineData(100, 200, 500, 500, 250, 500)] // grow portrait to square + public void FitWithin_Returns_CorrectSize(int w, int h, int targetWidth, int targetHeight, int expectedWidth, + int expectedHeight) + { + // Arrange + var size = new Size(w, h); + + // Act + var newSize = Size.FitWithin(new Size(targetWidth, targetHeight), size); + + // Assert + newSize.Height.Should().Be(expectedHeight); + newSize.Width.Should().Be(expectedWidth); + } + + [Theory] + [InlineData(10, 10, 10, 10)] // square same size + [InlineData(10, 5, 10, 5)] // landscape same size + [InlineData(5, 10, 5, 10)] // portrait same size + [InlineData(10, 5, 10, 10)] // landscape within + [InlineData(5, 10, 10, 10)] // portrait within + [InlineData(5, 5, 10, 10)] // both dimensions within + public void IsConfinedWithin_True_IfConfinedWithin(int width, int height, int confinedWidth, int confinedHeight) + { + // Arrange + var size = new Size(width, height); + var confineSize = new Size(confinedWidth, confinedHeight); + + // Assert + size.IsConfinedWithin(confineSize).Should().BeTrue(); + } + + [Theory] + [InlineData(11, 11, 10, 10)] // both dimensions out + [InlineData(11, 9, 10, 10)] // width outside, height inside + [InlineData(9, 11, 10, 10)] // height outside, width inside + public void IsConfinedWithin_False_IfNotConfinedWithin(int width, int height, int confinedWidth, int confinedHeight) + { + // Arrange + var size = new Size(width, height); + var confineSize = new Size(confinedWidth, confinedHeight); + + // Assert + size.IsConfinedWithin(confineSize).Should().BeFalse(); + } + + [Theory] + [InlineData(10, 10, 10)] + [InlineData(5, 10, 10)] + [InlineData(10, 5, 10)] + public void MaxDimension_Correct(int w, int h, int expected) + { + new Size(w, h).MaxDimension.Should().Be(expected); + } + + [Theory] + [InlineData(10, 10, ImageShape.Square)] + [InlineData(5, 10, ImageShape.Portrait)] + [InlineData(10, 5, ImageShape.Landscape)] + public void GetShape_Correct(int w, int h, ImageShape expected) + { + new Size(w, h).GetShape().Should().Be(expected); + } + + [Fact] + public void GetSizeIncreasePercent_Correct_SameSize() + { + // Arrange + var large = new Size(100, 200); + var small = new Size(100, 200); + + // Act + var difference = Size.GetSizeIncreasePercent(large, small); + + // Assert + difference.Should().Be(0); + } + + [Fact] + public void GetSizeIncreasePercent_Correct() + { + // Arrange + var large = new Size(300, 400); + var small = new Size(100, 200); + + // Act + var difference = Size.GetSizeIncreasePercent(large, small); + + // Assert + difference.Should().Be(100); + } + + [Fact] + public void GetSizeIncreasePercent_Throws_IfSmallerLarger() + { + // Arrange + var large = new Size(300, 400); + var small = new Size(100, 800); + + // Act + Action action = () => Size.GetSizeIncreasePercent(large, small); + + // Assert + action.Should().Throw(); + } + + private static List sampleTestData = new() + { + // currW, currH, confW, confH, expectedW, expectedH + new TestSizeData(200, 200, 300, 300, 200, 200), // actual smaller than confine + new TestSizeData(500, 500, 300, 300, 300, 300), // confined square + new TestSizeData(500, 400, 300, 300, 300, 240), // current portrait + new TestSizeData(400, 500, 300, 300, 240, 300), // current landscape + new TestSizeData(500, 500, 300, 200, 200, 200), // target portrait + new TestSizeData(500, 500, 200, 300, 200, 300), // target landscape + new TestSizeData(4553, 5668, 200, 200, 161, 200) // a specific rounding issue with Appetiser + }; + + // Test data for square confine targets only + public static IEnumerable ConfineDataSquare + { + get { - get - { - var data = sampleTestData.Where(d => d.ConfineWidth == d.ConfineHeight); - - var retVal = new List(); - retVal.AddRange(data.Select(sizeData => new object[] {sizeData})); - return retVal; - } + var data = sampleTestData.Where(d => d.ConfineWidth == d.ConfineHeight); + + var retVal = new List(); + retVal.AddRange(data.Select(sizeData => new object[] { sizeData })); + return retVal; } - - // Test data for square and non-square confine targets - public static IEnumerable ConfineData + } + + // Test data for square and non-square confine targets + public static IEnumerable ConfineData + { + get { - get - { - var data = sampleTestData.Where(d => d.ConfineWidth == d.ConfineHeight); - - var retVal = new List(); - retVal.AddRange(data.Select(sizeData => new object[] {sizeData})); - return retVal; - } + var data = sampleTestData.Where(d => d.ConfineWidth == d.ConfineHeight); + + var retVal = new List(); + retVal.AddRange(data.Select(sizeData => new object[] { sizeData })); + return retVal; } + } + + public class TestSizeData + { + public int CurrentWidth { get; } + public int CurrentHeight { get; } + public int ConfineWidth { get; } + public int ConfineHeight { get; } + public int ExpectedWidth { get; } + public int ExpectedHeight { get; } - public class TestSizeData + public TestSizeData(int currentWidth, int currentHeight, int confineWidth, int confineHeight, + int expectedWidth, int expectedHeight) { - public int CurrentWidth { get; } - public int CurrentHeight { get; } - public int ConfineWidth { get; } - public int ConfineHeight { get; } - public int ExpectedWidth { get; } - public int ExpectedHeight { get; } - - public TestSizeData(int currentWidth, int currentHeight, int confineWidth, int confineHeight, - int expectedWidth, int expectedHeight) - { - CurrentWidth = currentWidth; - CurrentHeight = currentHeight; - ConfineWidth = confineWidth; - ConfineHeight = confineHeight; - ExpectedWidth = expectedWidth; - ExpectedHeight = expectedHeight; - } + CurrentWidth = currentWidth; + CurrentHeight = currentHeight; + ConfineWidth = confineWidth; + ConfineHeight = confineHeight; + ExpectedWidth = expectedWidth; + ExpectedHeight = expectedHeight; } } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/ImageApi/V2/ImageService2Tests.cs b/src/IIIF/IIIF.Tests/ImageApi/V2/ImageService2Tests.cs index 66d83b6..61d5dde 100644 --- a/src/IIIF/IIIF.Tests/ImageApi/V2/ImageService2Tests.cs +++ b/src/IIIF/IIIF.Tests/ImageApi/V2/ImageService2Tests.cs @@ -2,33 +2,32 @@ using IIIF.ImageApi.V2; using Xunit; -namespace IIIF.Tests.ImageApi.V2 +namespace IIIF.Tests.ImageApi.V2; + +public class ImageService2Tests { - public class ImageService2Tests + [Fact] + public void Type_Default_IfNotSet() + { + // Arrange + var imageService = new ImageService2(); + + // Assert + imageService.Type.Should().Be("ImageService2"); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + [InlineData("SomeType")] + public void Type_ReturnsSetValue_IfSet(string type) { - [Fact] - public void Type_Default_IfNotSet() - { - // Arrange - var imageService = new ImageService2(); - - // Assert - imageService.Type.Should().Be("ImageService2"); - } - - [Theory] - [InlineData(null)] - [InlineData("")] - [InlineData(" ")] - [InlineData("SomeType")] - public void Type_ReturnsSetValue_IfSet(string type) - { - // Arrange - var imageService = new ImageService2(); - imageService.Type = type; - - // Assert - imageService.Type.Should().Be(type); - } + // Arrange + var imageService = new ImageService2(); + imageService.Type = type; + + // Assert + imageService.Type.Should().Be(type); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/ImageApi/VersionTests.cs b/src/IIIF/IIIF.Tests/ImageApi/VersionTests.cs index 1c83a3d..285dfc4 100644 --- a/src/IIIF/IIIF.Tests/ImageApi/VersionTests.cs +++ b/src/IIIF/IIIF.Tests/ImageApi/VersionTests.cs @@ -4,22 +4,21 @@ using IIIF.ImageApi; using Xunit; -namespace IIIF.Tests.ImageApi +namespace IIIF.Tests.ImageApi; + +public class VersionTests { - public class VersionTests + [Theory] + [InlineData(Version.Unknown, "Unknown")] + [InlineData(Version.V2, "http://iiif.io/api/image/2/context.json")] + [InlineData(Version.V3, "http://iiif.io/api/image/3/context.json")] + public void VersionsHaveCorrectDescriptions(Version version, string context) { - [Theory] - [InlineData(Version.Unknown, "Unknown")] - [InlineData(Version.V2, "http://iiif.io/api/image/2/context.json")] - [InlineData(Version.V3, "http://iiif.io/api/image/3/context.json")] - public void VersionsHaveCorrectDescriptions(Version version, string context) - { - var fieldInfo = typeof(Version).GetField(version.ToString()); - var actual = fieldInfo! - .GetCustomAttribute()! - .Description; + var fieldInfo = typeof(Version).GetField(version.ToString()); + var actual = fieldInfo! + .GetCustomAttribute()! + .Description; - actual.Should().Be(context); - } + actual.Should().Be(context); } -} +} \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Presentation/V2/MetadataValueTests.cs b/src/IIIF/IIIF.Tests/Presentation/V2/MetadataValueTests.cs index f3bab2e..54916b0 100644 --- a/src/IIIF/IIIF.Tests/Presentation/V2/MetadataValueTests.cs +++ b/src/IIIF/IIIF.Tests/Presentation/V2/MetadataValueTests.cs @@ -4,136 +4,137 @@ using IIIF.Presentation.V3.Strings; using Xunit; -namespace IIIF.Tests.Presentation.V2 +namespace IIIF.Tests.Presentation.V2; + +public class MetadataValueTests { - public class MetadataValueTests + [Fact] + public void Create_ReturnsNull_IfPassedNull() { - [Fact] - public void Create_ReturnsNull_IfPassedNull() - => MetaDataValue.Create(null).Should().BeNull(); + MetaDataValue.Create(null).Should().BeNull(); + } - [Fact] - public void Create_LanguageValuesNullOrEmpty_IfPassedEmptyMap() - { - // Arrange - var languageMap = new LanguageMap(); - - // Act - var metadataValue = MetaDataValue.Create(languageMap); - - // Assert - metadataValue.LanguageValues.Should().BeNullOrEmpty(); - } - - [Fact] - public void Create_Handles_SingleLanguageSingleValue() + [Fact] + public void Create_LanguageValuesNullOrEmpty_IfPassedEmptyMap() + { + // Arrange + var languageMap = new LanguageMap(); + + // Act + var metadataValue = MetaDataValue.Create(languageMap); + + // Assert + metadataValue.LanguageValues.Should().BeNullOrEmpty(); + } + + [Fact] + public void Create_Handles_SingleLanguageSingleValue() + { + // Arrange + var languageMap = new LanguageMap("en", "foo bar"); + + var expected = new List { - // Arrange - var languageMap = new LanguageMap("en", "foo bar"); - - var expected = new List - { - new() {Language = "en", Value = "foo bar"}, - }; - - // Act - var metadataValue = MetaDataValue.Create(languageMap); - - // Assert - metadataValue.LanguageValues.Should().BeEquivalentTo(expected).And.HaveCount(1); - } - - [Fact] - public void Create_Handles_SingleLanguageMultiValue() + new() { Language = "en", Value = "foo bar" } + }; + + // Act + var metadataValue = MetaDataValue.Create(languageMap); + + // Assert + metadataValue.LanguageValues.Should().BeEquivalentTo(expected).And.HaveCount(1); + } + + [Fact] + public void Create_Handles_SingleLanguageMultiValue() + { + // Arrange + var languageMap = new LanguageMap("en", new[] { "foo", "bar" }); + + var expected = new List { - // Arrange - var languageMap = new LanguageMap("en", new[] {"foo", "bar"}); - - var expected = new List - { - new() {Language = "en", Value = "foo"}, - new() {Language = "en", Value = "bar"} - }; - - // Act - var metadataValue = MetaDataValue.Create(languageMap); - - // Assert - metadataValue.LanguageValues.Should().BeEquivalentTo(expected).And.HaveCount(2); - } - - [Fact] - public void Create_Handles_MultiLanguage() + new() { Language = "en", Value = "foo" }, + new() { Language = "en", Value = "bar" } + }; + + // Act + var metadataValue = MetaDataValue.Create(languageMap); + + // Assert + metadataValue.LanguageValues.Should().BeEquivalentTo(expected).And.HaveCount(2); + } + + [Fact] + public void Create_Handles_MultiLanguage() + { + // Arrange + var languageMap = new LanguageMap("en", "foo"); + languageMap["fr"] = new List { "bar" }; + + var expected = new List { - // Arrange - var languageMap = new LanguageMap("en", "foo"); - languageMap["fr"] = new List {"bar"}; - - var expected = new List - { - new() {Language = "en", Value = "foo"}, - new() {Language = "fr", Value = "bar"} - }; - - // Act - var metadataValue = MetaDataValue.Create(languageMap); - - // Assert - metadataValue.LanguageValues.Should().BeEquivalentTo(expected).And.HaveCount(2); - } - - [Fact] - public void Create_IgnoresLanguageIfNone() + new() { Language = "en", Value = "foo" }, + new() { Language = "fr", Value = "bar" } + }; + + // Act + var metadataValue = MetaDataValue.Create(languageMap); + + // Assert + metadataValue.LanguageValues.Should().BeEquivalentTo(expected).And.HaveCount(2); + } + + [Fact] + public void Create_IgnoresLanguageIfNone() + { + // Arrange + var languageMap = new LanguageMap("none", "foo bar"); + + var expected = new List { - // Arrange - var languageMap = new LanguageMap("none", "foo bar"); - - var expected = new List - { - new() {Language = null, Value = "foo bar"}, - }; - - // Act - var metadataValue = MetaDataValue.Create(languageMap); - - // Assert - metadataValue.LanguageValues.Should().BeEquivalentTo(expected).And.HaveCount(1); - } - - [Fact] - public void Create_IgnoresLanguageIf_IgnoreLanguageTrue() + new() { Language = null, Value = "foo bar" } + }; + + // Act + var metadataValue = MetaDataValue.Create(languageMap); + + // Assert + metadataValue.LanguageValues.Should().BeEquivalentTo(expected).And.HaveCount(1); + } + + [Fact] + public void Create_IgnoresLanguageIf_IgnoreLanguageTrue() + { + // Arrange + var languageMap = new LanguageMap("en", "foo bar"); + + var expected = new List { - // Arrange - var languageMap = new LanguageMap("en", "foo bar"); - - var expected = new List - { - new() {Value = "foo bar"}, - }; - - // Act - var metadataValue = MetaDataValue.Create(languageMap, true); - - // Assert - metadataValue.LanguageValues.Should().BeEquivalentTo(expected).And.HaveCount(1); - } - - [Fact] - public void Create_CanFilterWithPredicate() + new() { Value = "foo bar" } + }; + + // Act + var metadataValue = MetaDataValue.Create(languageMap, true); + + // Assert + metadataValue.LanguageValues.Should().BeEquivalentTo(expected).And.HaveCount(1); + } + + [Fact] + public void Create_CanFilterWithPredicate() + { + // Arrange + var languageMap = new LanguageMap("en", new[] { "foo", "bar" }); + + var expected = new List { - // Arrange - var languageMap = new LanguageMap("en", new[] {"foo", "bar"}); - - var expected = new List - { - new() {Language = "en", Value = "foo"} - }; - - // Act - var metadataValue = MetaDataValue.Create(languageMap, languagePredicate: s => s != "bar"); - - // Assert - metadataValue.LanguageValues.Should().BeEquivalentTo(expected).And.HaveCount(1); - } + new() { Language = "en", Value = "foo" } + }; + + // Act + var metadataValue = MetaDataValue.Create(languageMap, languagePredicate: s => s != "bar"); + + // Assert + metadataValue.LanguageValues.Should().BeEquivalentTo(expected).And.HaveCount(1); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Presentation/V2/MetadataXTests.cs b/src/IIIF/IIIF.Tests/Presentation/V2/MetadataXTests.cs index 40b1d1d..dd2e3f0 100644 --- a/src/IIIF/IIIF.Tests/Presentation/V2/MetadataXTests.cs +++ b/src/IIIF/IIIF.Tests/Presentation/V2/MetadataXTests.cs @@ -5,85 +5,84 @@ using IIIF.Presentation.V2.Strings; using Xunit; -namespace IIIF.Tests.Presentation.V2 +namespace IIIF.Tests.Presentation.V2; + +public class MetadataXTests { - public class MetadataXTests + [Fact] + public void GetValueByLabel_Null_IfMetadataNull() { - [Fact] - public void GetValueByLabel_Null_IfMetadataNull() - { - // Arrange - List metadata = null; - - // Act - var actual = metadata.GetValueByLabel("foo"); - - // Assert - actual.Should().BeNull(); - } - - [Fact] - public void GetValueByLabel_Null_IfMetadataEmpty() - { - // Arrange - List metadata = new(); - - // Act - var actual = metadata.GetValueByLabel("foo"); - - // Assert - actual.Should().BeNull(); - } - - [Fact] - public void GetValueByLabel_Null_IfMetadataLabelNotFound() + // Arrange + List metadata = null; + + // Act + var actual = metadata.GetValueByLabel("foo"); + + // Assert + actual.Should().BeNull(); + } + + [Fact] + public void GetValueByLabel_Null_IfMetadataEmpty() + { + // Arrange + List metadata = new(); + + // Act + var actual = metadata.GetValueByLabel("foo"); + + // Assert + actual.Should().BeNull(); + } + + [Fact] + public void GetValueByLabel_Null_IfMetadataLabelNotFound() + { + // Arrange + var metadata = new List { - // Arrange - var metadata = new List - { - new() {Label = new MetaDataValue("TestLabel"), Value = new MetaDataValue("TestValue")} - }; - - // Act - var actual = metadata.GetValueByLabel("foo"); - - // Assert - actual.Should().BeNull(); - } - - [Fact] - public void GetValueByLabel_ReturnsValue_IfFound() + new() { Label = new MetaDataValue("TestLabel"), Value = new MetaDataValue("TestValue") } + }; + + // Act + var actual = metadata.GetValueByLabel("foo"); + + // Assert + actual.Should().BeNull(); + } + + [Fact] + public void GetValueByLabel_ReturnsValue_IfFound() + { + // Arrange + var metadata = new List { - // Arrange - var metadata = new List - { - new() {Label = new MetaDataValue("TestLabel"), Value = new MetaDataValue("TestValue")} - }; - - // Act - var actual = metadata.GetValueByLabel("TestLabel"); - - // Assert - actual.Should().Be("TestValue"); - } - - [Fact] - public void GetValueByLabel_Throws_IfMultipleItemsWithLabel() + new() { Label = new MetaDataValue("TestLabel"), Value = new MetaDataValue("TestValue") } + }; + + // Act + var actual = metadata.GetValueByLabel("TestLabel"); + + // Assert + actual.Should().Be("TestValue"); + } + + [Fact] + public void GetValueByLabel_Throws_IfMultipleItemsWithLabel() + { + // Arrange + var metadata = new List { - // Arrange - var metadata = new List - { - new() {Label = new MetaDataValue("TestLabel"), Value = new MetaDataValue("TestValue")}, - new() {Label = new MetaDataValue("TestLabel"), Value = new MetaDataValue("Another Value")} - }; - - // Act - Action action = () => metadata.GetValueByLabel("TestLabel"); - - // Assert - action.Should() - .Throw() - .WithMessage("Sequence contains more than one matching element"); - } + new() { Label = new MetaDataValue("TestLabel"), Value = new MetaDataValue("TestValue") }, + new() { Label = new MetaDataValue("TestLabel"), Value = new MetaDataValue("Another Value") } + }; + + // Act + Action action = () => metadata.GetValueByLabel("TestLabel"); + + // Assert + action.Should() + .Throw() + .WithMessage("Sequence contains more than one matching element"); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Presentation/V2/Serialisation/MetaDataValueSerialiserTests.cs b/src/IIIF/IIIF.Tests/Presentation/V2/Serialisation/MetaDataValueSerialiserTests.cs index 6a59737..631968e 100644 --- a/src/IIIF/IIIF.Tests/Presentation/V2/Serialisation/MetaDataValueSerialiserTests.cs +++ b/src/IIIF/IIIF.Tests/Presentation/V2/Serialisation/MetaDataValueSerialiserTests.cs @@ -6,186 +6,186 @@ using Newtonsoft.Json; using Xunit; -namespace IIIF.Tests.Presentation.V2.Serialisation +namespace IIIF.Tests.Presentation.V2.Serialisation; + +public class MetaDataValueSerialiserTests { - public class MetaDataValueSerialiserTests + private readonly MetaDataValueSerialiser sut; + + public MetaDataValueSerialiserTests() + { + sut = new MetaDataValueSerialiser(); + } + + [Fact] + public void WriteJson_Throws_IfNoLanguageValues() + { + // Arrange + var metadata = new MetaDataValue(Enumerable.Empty()); + + // Act + Action action = () => JsonConvert.SerializeObject(metadata, sut); + + // Assert + action.Should().Throw(); + } + + [Fact] + public void WriteJson_WritesSingleValue_IfNoLanguage_SingleValue() + { + // Arrange + var metadata = new MetaDataValue("Foo bar"); + var expected = "\"Foo bar\""; + + // Act + var result = JsonConvert.SerializeObject(metadata, sut); + + // Assert + result.Should().Be(expected); + } + + [Fact] + public void WriteJson_WritesMultipleValues_IfNoLanguage_MultipleValues() + { + // Arrange + var metadata = new MetaDataValue("foo"); + metadata.LanguageValues.Add(new LanguageValue { Value = "bar" }); + var expected = "[\"foo\",\"bar\"]"; + + // Act + var result = JsonConvert.SerializeObject(metadata, sut); + + // Assert + result.Should().Be(expected); + } + + [Fact] + public void WriteJson_WritesLanguageAndValue_IfLanguage_SingleValue() + { + // Arrange + var metadata = new MetaDataValue("Foo bar", "en"); + var expected = "{\"@value\":\"Foo bar\",\"@language\":\"en\"}"; + + // Act + var result = JsonConvert.SerializeObject(metadata, sut); + + // Assert + result.Should().Be(expected); + } + + [Fact] + public void WriteJson_WritesLanguageAndValue_IfMultipleValues_SingleLanguage() + { + // Arrange + var metadata = new MetaDataValue("foo", "en"); + metadata.LanguageValues.Add(new LanguageValue { Value = "bar", Language = "en" }); + var expected = "[{\"@value\":\"foo\",\"@language\":\"en\"},{\"@value\":\"bar\",\"@language\":\"en\"}]"; + + // Act + var result = JsonConvert.SerializeObject(metadata, sut); + + // Assert + result.Should().Be(expected); + } + + [Fact] + public void WriteJson_WritesLanguageAndValue_IfMultipleLanguages() + { + // Arrange + var metadata = new MetaDataValue("foo", "en"); + metadata.LanguageValues.Add(new LanguageValue { Value = "bar", Language = "fr" }); + var expected = "[{\"@value\":\"foo\",\"@language\":\"en\"},{\"@value\":\"bar\",\"@language\":\"fr\"}]"; + + // Act + var result = JsonConvert.SerializeObject(metadata, sut); + + // Assert + result.Should().Be(expected); + } + + [Fact] + public void ReadJson_SingleValue_NoLanguage() + { + // Arrange + const string metadata = "\"Foo bar\""; + var expected = new MetaDataValue("Foo bar"); + + // Act + var result = JsonConvert.DeserializeObject(metadata); + + // Assert + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public void ReadJson_MultipleValues_NoLanguage() + { + // Arrange + const string metadata = "[\"foo\",\"bar\"]"; + var expected = new MetaDataValue("foo"); + expected.LanguageValues.Add(new LanguageValue { Value = "bar" }); + + // Act + var result = JsonConvert.DeserializeObject(metadata); + + // Assert + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public void ReadJson_SingleValue_SingleLanguage() { - private readonly MetaDataValueSerialiser sut; - public MetaDataValueSerialiserTests() - { - sut = new MetaDataValueSerialiser(); - } - - [Fact] - public void WriteJson_Throws_IfNoLanguageValues() - { - // Arrange - var metadata = new MetaDataValue(Enumerable.Empty()); - - // Act - Action action = () => JsonConvert.SerializeObject(metadata, sut); - - // Assert - action.Should().Throw(); - } - - [Fact] - public void WriteJson_WritesSingleValue_IfNoLanguage_SingleValue() - { - // Arrange - var metadata = new MetaDataValue("Foo bar"); - var expected = "\"Foo bar\""; - - // Act - var result = JsonConvert.SerializeObject(metadata, sut); - - // Assert - result.Should().Be(expected); - } - - [Fact] - public void WriteJson_WritesMultipleValues_IfNoLanguage_MultipleValues() - { - // Arrange - var metadata = new MetaDataValue("foo"); - metadata.LanguageValues.Add(new LanguageValue{Value = "bar"}); - var expected = "[\"foo\",\"bar\"]"; - - // Act - var result = JsonConvert.SerializeObject(metadata, sut); - - // Assert - result.Should().Be(expected); - } - - [Fact] - public void WriteJson_WritesLanguageAndValue_IfLanguage_SingleValue() - { - // Arrange - var metadata = new MetaDataValue("Foo bar", "en"); - var expected = "{\"@value\":\"Foo bar\",\"@language\":\"en\"}"; - - // Act - var result = JsonConvert.SerializeObject(metadata, sut); - - // Assert - result.Should().Be(expected); - } - - [Fact] - public void WriteJson_WritesLanguageAndValue_IfMultipleValues_SingleLanguage() - { - // Arrange - var metadata = new MetaDataValue("foo", "en"); - metadata.LanguageValues.Add(new LanguageValue{Value = "bar", Language = "en"}); - var expected = "[{\"@value\":\"foo\",\"@language\":\"en\"},{\"@value\":\"bar\",\"@language\":\"en\"}]"; - - // Act - var result = JsonConvert.SerializeObject(metadata, sut); - - // Assert - result.Should().Be(expected); - } - - [Fact] - public void WriteJson_WritesLanguageAndValue_IfMultipleLanguages() - { - // Arrange - var metadata = new MetaDataValue("foo", "en"); - metadata.LanguageValues.Add(new LanguageValue{Value = "bar", Language = "fr"}); - var expected = "[{\"@value\":\"foo\",\"@language\":\"en\"},{\"@value\":\"bar\",\"@language\":\"fr\"}]"; - - // Act - var result = JsonConvert.SerializeObject(metadata, sut); - - // Assert - result.Should().Be(expected); - } - - [Fact] - public void ReadJson_SingleValue_NoLanguage() - { - // Arrange - const string metadata = "\"Foo bar\""; - var expected = new MetaDataValue("Foo bar"); - - // Act - var result = JsonConvert.DeserializeObject(metadata); - - // Assert - result.Should().BeEquivalentTo(expected); - } - - [Fact] - public void ReadJson_MultipleValues_NoLanguage() - { - // Arrange - const string metadata = "[\"foo\",\"bar\"]"; - var expected = new MetaDataValue("foo"); - expected.LanguageValues.Add(new LanguageValue{Value = "bar"}); - - // Act - var result = JsonConvert.DeserializeObject(metadata); - - // Assert - result.Should().BeEquivalentTo(expected); - } - - [Fact] - public void ReadJson_SingleValue_SingleLanguage() - { - // Arrange - const string metadata = "{\"@value\":\"Foo bar\",\"@language\":\"en\"}"; - var expected = new MetaDataValue("Foo bar", "en"); - - // Act - var result = JsonConvert.DeserializeObject(metadata); - - // Assert - result.Should().BeEquivalentTo(expected); - } - - [Fact] - public void ReadJson_MultipleValues_SingleLanguage() - { - // Arrange - const string metadata = "[{\"@value\":\"foo\",\"@language\":\"en\"},{\"@value\":\"bar\",\"@language\":\"en\"}]"; - var expected = new MetaDataValue("foo", "en"); - expected.LanguageValues.Add(new LanguageValue{Value = "bar", Language = "en"}); - - // Act - var result = JsonConvert.DeserializeObject(metadata); - - // Assert - result.Should().BeEquivalentTo(expected); - } - - [Fact] - public void ReadJson_MultipleLanguages() - { - // Arrange - const string metadata = "[{\"@value\":\"foo\",\"@language\":\"en\"},{\"@value\":\"bar\",\"@language\":\"fr\"}]"; - var expected = new MetaDataValue("foo", "en"); - expected.LanguageValues.Add(new LanguageValue{Value = "bar", Language = "fr"}); - - // Act - var result = JsonConvert.DeserializeObject(metadata); - - // Assert - result.Should().BeEquivalentTo(expected); - } - - [Fact] - public void ReadJson_Throws_IfFormatUnknown() - { - // Arrange - const string metadata = "true"; - - // Act - Action action = () => JsonConvert.DeserializeObject(metadata); - - // Assert - action.Should().Throw(); - } + // Arrange + const string metadata = "{\"@value\":\"Foo bar\",\"@language\":\"en\"}"; + var expected = new MetaDataValue("Foo bar", "en"); + + // Act + var result = JsonConvert.DeserializeObject(metadata); + + // Assert + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public void ReadJson_MultipleValues_SingleLanguage() + { + // Arrange + const string metadata = "[{\"@value\":\"foo\",\"@language\":\"en\"},{\"@value\":\"bar\",\"@language\":\"en\"}]"; + var expected = new MetaDataValue("foo", "en"); + expected.LanguageValues.Add(new LanguageValue { Value = "bar", Language = "en" }); + + // Act + var result = JsonConvert.DeserializeObject(metadata); + + // Assert + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public void ReadJson_MultipleLanguages() + { + // Arrange + const string metadata = "[{\"@value\":\"foo\",\"@language\":\"en\"},{\"@value\":\"bar\",\"@language\":\"fr\"}]"; + var expected = new MetaDataValue("foo", "en"); + expected.LanguageValues.Add(new LanguageValue { Value = "bar", Language = "fr" }); + + // Act + var result = JsonConvert.DeserializeObject(metadata); + + // Assert + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public void ReadJson_Throws_IfFormatUnknown() + { + // Arrange + const string metadata = "true"; + + // Act + Action action = () => JsonConvert.DeserializeObject(metadata); + + // Assert + action.Should().Throw(); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Presentation/V3/CanvasTests.cs b/src/IIIF/IIIF.Tests/Presentation/V3/CanvasTests.cs index 2b7e4e9..b0ba442 100644 --- a/src/IIIF/IIIF.Tests/Presentation/V3/CanvasTests.cs +++ b/src/IIIF/IIIF.Tests/Presentation/V3/CanvasTests.cs @@ -7,48 +7,48 @@ using IIIF.Serialisation; using Xunit; -namespace IIIF.Tests.Presentation.V3 +namespace IIIF.Tests.Presentation.V3; + +public class CanvasTests { - public class CanvasTests + [Fact] + public void SerialiseTargetAsId_True_RendersIdAsTarget() { - [Fact] - public void SerialiseTargetAsId_True_RendersIdAsTarget() + var targetIsIdOnlyCanvas = new Canvas { - var targetIsIdOnlyCanvas = new Canvas - { - Id = "https://test.example.com/canvas/target-id-only", - Width = 1000, - Height = 1001, - SerialiseTargetAsId = true - }; + Id = "https://test.example.com/canvas/target-id-only", + Width = 1000, + Height = 1001, + SerialiseTargetAsId = true + }; - var referencingCanvas = new Canvas + var referencingCanvas = new Canvas + { + Id = "https://test.example.com/canvas/referencing", + Items = new List { - Id = "https://test.example.com/canvas/referencing", - Items = new List + new() { - new() + Id = "https://test.example.com/canvas/referencing/page", + Items = new List { - Id = "https://test.example.com/canvas/referencing/page", - Items = new List - { - new PaintingAnnotation { Target = targetIsIdOnlyCanvas, } - } + new PaintingAnnotation { Target = targetIsIdOnlyCanvas } } } - }; - - var manifest = new Manifest - { - Context = "http://iiif.io/api/presentation/3/context.json", - Id = "https://test.example.com/manifest", - Label = new LanguageMap("en", "Test string"), - Items = new List { targetIsIdOnlyCanvas, referencingCanvas } - }; + } + }; + + var manifest = new Manifest + { + Context = "http://iiif.io/api/presentation/3/context.json", + Id = "https://test.example.com/manifest", + Label = new LanguageMap("en", "Test string"), + Items = new List { targetIsIdOnlyCanvas, referencingCanvas } + }; - var serialisedManifest = manifest.AsJson().Replace("\r\n", "\n"); + var serialisedManifest = manifest.AsJson().Replace("\r\n", "\n"); - const string expected = @"{ + const string expected = @"{ ""@context"": ""http://iiif.io/api/presentation/3/context.json"", ""id"": ""https://test.example.com/manifest"", ""type"": ""Manifest"", @@ -79,47 +79,47 @@ public void SerialiseTargetAsId_True_RendersIdAsTarget() } ] }"; - - serialisedManifest.Should().BeEquivalentTo(expected); - } - - [Fact] - public void SerialiseTargetAsId_False_RendersFullCanvasAsTarget() + + serialisedManifest.Should().BeEquivalentTo(expected); + } + + [Fact] + public void SerialiseTargetAsId_False_RendersFullCanvasAsTarget() + { + var targetIsFullCanvas = new Canvas { - var targetIsFullCanvas = new Canvas - { - Id = "https://test.example.com/canvas/full", - Width = 1000, - Height = 1001, - }; + Id = "https://test.example.com/canvas/full", + Width = 1000, + Height = 1001 + }; - var referencingCanvas = new Canvas + var referencingCanvas = new Canvas + { + Id = "https://test.example.com/canvas/referencing", + Items = new List { - Id = "https://test.example.com/canvas/referencing", - Items = new List + new() { - new() + Id = "https://test.example.com/canvas/referencing/page", + Items = new List { - Id = "https://test.example.com/canvas/referencing/page", - Items = new List - { - new PaintingAnnotation { Target = targetIsFullCanvas, } - } + new PaintingAnnotation { Target = targetIsFullCanvas } } } - }; - - var manifest = new Manifest - { - Context = "http://iiif.io/api/presentation/3/context.json", - Id = "https://test.example.com/manifest", - Label = new LanguageMap("en", "Test string"), - Items = new List { targetIsFullCanvas, referencingCanvas } - }; + } + }; - var serialisedManifest = manifest.AsJson().Replace("\r\n", "\n"); + var manifest = new Manifest + { + Context = "http://iiif.io/api/presentation/3/context.json", + Id = "https://test.example.com/manifest", + Label = new LanguageMap("en", "Test string"), + Items = new List { targetIsFullCanvas, referencingCanvas } + }; - const string expected = @"{ + var serialisedManifest = manifest.AsJson().Replace("\r\n", "\n"); + + const string expected = @"{ ""@context"": ""http://iiif.io/api/presentation/3/context.json"", ""id"": ""https://test.example.com/manifest"", ""type"": ""Manifest"", @@ -155,46 +155,46 @@ public void SerialiseTargetAsId_False_RendersFullCanvasAsTarget() } ] }"; - - serialisedManifest.Should().BeEquivalentTo(expected); - } - - [Fact] - public void CanSelfReferenceCanvas() + + serialisedManifest.Should().BeEquivalentTo(expected); + } + + [Fact] + public void CanSelfReferenceCanvas() + { + var canvas = new Canvas { - var canvas = new Canvas - { - Id = "https://test.example.com/canvas/target-id-only", - SerialiseTargetAsId = true - }; + Id = "https://test.example.com/canvas/target-id-only", + SerialiseTargetAsId = true + }; - canvas.Items = new List + canvas.Items = new List + { + new() { - new() + Id = "https://test.example.com/canvas/referencing/page", + Items = new List { - Id = "https://test.example.com/canvas/referencing/page", - Items = new List + new PaintingAnnotation { - new PaintingAnnotation - { - Id = "https://test.example.com/canvas/referencing/page/anno", - Target = canvas, - } + Id = "https://test.example.com/canvas/referencing/page/anno", + Target = canvas } } - }; + } + }; - var manifest = new Manifest - { - Context = "http://iiif.io/api/presentation/3/context.json", - Id = "https://test.example.com/manifest", - Label = new LanguageMap("en", "Test string"), - Items = new List { canvas } - }; + var manifest = new Manifest + { + Context = "http://iiif.io/api/presentation/3/context.json", + Id = "https://test.example.com/manifest", + Label = new LanguageMap("en", "Test string"), + Items = new List { canvas } + }; - var serialisedManifest = manifest.AsJson().Replace("\r\n", "\n"); + var serialisedManifest = manifest.AsJson().Replace("\r\n", "\n"); - const string expected = @"{ + const string expected = @"{ ""@context"": ""http://iiif.io/api/presentation/3/context.json"", ""id"": ""https://test.example.com/manifest"", ""type"": ""Manifest"", @@ -220,8 +220,7 @@ public void CanSelfReferenceCanvas() } ] }"; - - serialisedManifest.Should().BeEquivalentTo(expected); - } + + serialisedManifest.Should().BeEquivalentTo(expected); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Presentation/V3/ManifestXTests.cs b/src/IIIF/IIIF.Tests/Presentation/V3/ManifestXTests.cs index 4032578..eb2da31 100644 --- a/src/IIIF/IIIF.Tests/Presentation/V3/ManifestXTests.cs +++ b/src/IIIF/IIIF.Tests/Presentation/V3/ManifestXTests.cs @@ -3,93 +3,92 @@ using IIIF.Presentation.V3; using Xunit; -namespace IIIF.Tests.Presentation.V3 +namespace IIIF.Tests.Presentation.V3; + +public class ManifestXTests { - public class ManifestXTests + [Fact] + public void ContainsAV_False_IfItemsNull() { - [Fact] - public void ContainsAV_False_IfItemsNull() - { - // Arrange - var manifest = new Manifest(); - - // Act - var result = manifest.ContainsAV(); - - // Assert - result.Should().BeFalse(); - } - - [Fact] - public void ContainsAV_False_IfItemsEmpty() - { - // Arrange - var manifest = new Manifest {Items = new List()}; - - // Act - var result = manifest.ContainsAV(); - - // Assert - result.Should().BeFalse(); - } - - [Fact] - public void ContainsAV_False_IfItemsNullDuration() + // Arrange + var manifest = new Manifest(); + + // Act + var result = manifest.ContainsAV(); + + // Assert + result.Should().BeFalse(); + } + + [Fact] + public void ContainsAV_False_IfItemsEmpty() + { + // Arrange + var manifest = new Manifest { Items = new List() }; + + // Act + var result = manifest.ContainsAV(); + + // Assert + result.Should().BeFalse(); + } + + [Fact] + public void ContainsAV_False_IfItemsNullDuration() + { + // Arrange + var manifest = new Manifest { - // Arrange - var manifest = new Manifest + Items = new List { - Items = new List - { - new() {Id = "test", Duration = null} - } - }; - - // Act - var result = manifest.ContainsAV(); - - // Assert - result.Should().BeFalse(); - } - - [Fact] - public void ContainsAV_False_IfItems0Duration() + new() { Id = "test", Duration = null } + } + }; + + // Act + var result = manifest.ContainsAV(); + + // Assert + result.Should().BeFalse(); + } + + [Fact] + public void ContainsAV_False_IfItems0Duration() + { + // Arrange + var manifest = new Manifest { - // Arrange - var manifest = new Manifest + Items = new List { - Items = new List - { - new() {Id = "test", Duration = 0} - } - }; - - // Act - var result = manifest.ContainsAV(); - - // Assert - result.Should().BeFalse(); - } - - [Fact] - public void ContainsAV_True_IfItemsContainsAbove0Duration() + new() { Id = "test", Duration = 0 } + } + }; + + // Act + var result = manifest.ContainsAV(); + + // Assert + result.Should().BeFalse(); + } + + [Fact] + public void ContainsAV_True_IfItemsContainsAbove0Duration() + { + // Arrange + var manifest = new Manifest { - // Arrange - var manifest = new Manifest + Items = new List { - Items = new List - { - new() {Id = "test", Duration = null}, - new() {Id = "test", Duration = 0}, - new() {Id = "test", Duration = 1}, - } - }; - - // Act - var result = manifest.ContainsAV(); - - // Assert - result.Should().BeTrue(); - } + new() { Id = "test", Duration = null }, + new() { Id = "test", Duration = 0 }, + new() { Id = "test", Duration = 1 } + } + }; + + // Act + var result = manifest.ContainsAV(); + + // Assert + result.Should().BeTrue(); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Presentation/VersionTests.cs b/src/IIIF/IIIF.Tests/Presentation/VersionTests.cs index 66f1d0c..84534c1 100644 --- a/src/IIIF/IIIF.Tests/Presentation/VersionTests.cs +++ b/src/IIIF/IIIF.Tests/Presentation/VersionTests.cs @@ -4,22 +4,21 @@ using IIIF.Presentation; using Xunit; -namespace IIIF.Tests.Presentation +namespace IIIF.Tests.Presentation; + +public class VersionTests { - public class VersionTests + [Theory] + [InlineData(Version.Unknown, "Unknown")] + [InlineData(Version.V2, "http://iiif.io/api/presentation/2/context.json")] + [InlineData(Version.V3, "http://iiif.io/api/presentation/3/context.json")] + public void VersionsHaveCorrectDescriptions(Version version, string context) { - [Theory] - [InlineData(Version.Unknown, "Unknown")] - [InlineData(Version.V2, "http://iiif.io/api/presentation/2/context.json")] - [InlineData(Version.V3, "http://iiif.io/api/presentation/3/context.json")] - public void VersionsHaveCorrectDescriptions(Version version, string context) - { - var fieldInfo = typeof(Version).GetField(version.ToString()); - var actual = fieldInfo! - .GetCustomAttribute()! - .Description; + var fieldInfo = typeof(Version).GetField(version.ToString()); + var actual = fieldInfo! + .GetCustomAttribute()! + .Description; - actual.Should().Be(context); - } + actual.Should().Be(context); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Serialisation/DiscoveryTests.cs b/src/IIIF/IIIF.Tests/Serialisation/DiscoveryTests.cs index 0194d4c..81cbb24 100644 --- a/src/IIIF/IIIF.Tests/Serialisation/DiscoveryTests.cs +++ b/src/IIIF/IIIF.Tests/Serialisation/DiscoveryTests.cs @@ -7,16 +7,16 @@ using IIIF.Serialisation; using Xunit; -namespace IIIF.Tests.Serialisation +namespace IIIF.Tests.Serialisation; + +public class DiscoveryTests { - public class DiscoveryTests + [Fact] + public void OrderedCollection() { - [Fact] - public void OrderedCollection() - { - // Arrange - // Example from https://iiif.io/api/discovery/1.0/#complete-ordered-collection-example - var expected = @"{ + // Arrange + // Example from https://iiif.io/api/discovery/1.0/#complete-ordered-collection-example + var expected = @"{ ""@context"": ""http://iiif.io/api/discovery/1/context.json"", ""id"": ""https://example.org/activity/all-changes"", ""type"": ""OrderedCollection"", @@ -46,43 +46,43 @@ public void OrderedCollection() ""type"": ""OrderedCollectionPage"" } }"; - var orderedCollection = new OrderedCollection + var orderedCollection = new OrderedCollection + { + Id = "https://example.org/activity/all-changes", + TotalItems = 21456, + Rights = "http://creativecommons.org/licenses/by/4.0/", + SeeAlso = new List { - Id = "https://example.org/activity/all-changes", - TotalItems = 21456, - Rights = "http://creativecommons.org/licenses/by/4.0/", - SeeAlso = new List - { new("Dataset") { - Id = "https://example.org/dataset/all-dcat.jsonld", - Label = new LanguageMap("en", "DCAT description of Collection"), - Format = "application/ld+json", - Profile = "http://www.w3.org/ns/dcat#" + Id = "https://example.org/dataset/all-dcat.jsonld", + Label = new LanguageMap("en", "DCAT description of Collection"), + Format = "application/ld+json", + Profile = "http://www.w3.org/ns/dcat#" } - }, - PartOf = new List - { + }, + PartOf = new List + { new() { Id = "https://example.org/aggregated-changes" } - }, - First = new OrderedCollectionPage { Id = "https://example.org/activity/page-0" }, - Last = new OrderedCollectionPage { Id = "https://example.org/activity/page-214" }, - }; - orderedCollection.EnsureContext(Discovery.Context.ChangeDiscovery1Context); + }, + First = new OrderedCollectionPage { Id = "https://example.org/activity/page-0" }, + Last = new OrderedCollectionPage { Id = "https://example.org/activity/page-214" } + }; + orderedCollection.EnsureContext(Discovery.Context.ChangeDiscovery1Context); - // Act - var json = orderedCollection.AsJson(); - - // Assert - json.ShouldMatchJson(expected); - } - - [Fact] - public void OrderedCollectionPage() - { - // Arrange - // Example from https://iiif.io/api/discovery/1.0/#complete-ordered-collection-page-example - var expected = @"{ + // Act + var json = orderedCollection.AsJson(); + + // Assert + json.ShouldMatchJson(expected); + } + + [Fact] + public void OrderedCollectionPage() + { + // Arrange + // Example from https://iiif.io/api/discovery/1.0/#complete-ordered-collection-page-example + var expected = @"{ ""@context"": ""http://iiif.io/api/discovery/1/context.json"", ""id"": ""https://example.org/activity/page-1"", ""type"": ""OrderedCollectionPage"", @@ -110,42 +110,42 @@ public void OrderedCollectionPage() } ] }"; - var orderedCollectionPage = new OrderedCollectionPage + var orderedCollectionPage = new OrderedCollectionPage + { + Id = "https://example.org/activity/page-1", + StartIndex = 20, + PartOf = new OrderedCollection { Id = "https://example.org/activity/all-changes" }, + Prev = new OrderedCollectionPage { Id = "https://example.org/activity/page-0" }, + Next = new OrderedCollectionPage { Id = "https://example.org/activity/page-2" }, + OrderedItems = new List { - Id = "https://example.org/activity/page-1", - StartIndex = 20, - PartOf = new OrderedCollection { Id = "https://example.org/activity/all-changes" }, - Prev = new OrderedCollectionPage { Id = "https://example.org/activity/page-0" }, - Next = new OrderedCollectionPage { Id = "https://example.org/activity/page-2" }, - OrderedItems = new List - { new() { - Type = ActivityType.Update, - Object = new ActivityObject - { - Id = "https://example.org/iiif/1/manifest", - Type = "Manifest", - }, - EndTime = new DateTime(2018, 3, 10, 10, 0, 0) + Type = ActivityType.Update, + Object = new ActivityObject + { + Id = "https://example.org/iiif/1/manifest", + Type = "Manifest" + }, + EndTime = new DateTime(2018, 3, 10, 10, 0, 0) } - } - }; - orderedCollectionPage.EnsureContext(Discovery.Context.ChangeDiscovery1Context); + } + }; + orderedCollectionPage.EnsureContext(Discovery.Context.ChangeDiscovery1Context); - // Act - var json = orderedCollectionPage.AsJson(); - - // Assert - json.ShouldMatchJson(expected); - } - - [Fact] - public void Activity() - { - // Arrange - // Example from https://iiif.io/api/discovery/1.0/#complete-activity-example - var expected = @"{ + // Act + var json = orderedCollectionPage.AsJson(); + + // Assert + json.ShouldMatchJson(expected); + } + + [Fact] + public void Activity() + { + // Arrange + // Example from https://iiif.io/api/discovery/1.0/#complete-activity-example + var expected = @"{ ""@context"": ""http://iiif.io/api/discovery/1/context.json"", ""id"": ""https://example.org/activity/1"", ""type"": ""Update"", @@ -169,37 +169,36 @@ public void Activity() ""type"": ""Person"" } }"; - var activity = new Activity + var activity = new Activity + { + Id = "https://example.org/activity/1", + Type = ActivityType.Update, + Summary = "admin updated the manifest, fixing reported bug #15.", + Object = new ActivityObject { - Id = "https://example.org/activity/1", - Type = ActivityType.Update, - Summary = "admin updated the manifest, fixing reported bug #15.", - Object = new ActivityObject - { Id = "https://example.org/iiif/1/manifest", Type = "Manifest", Canonical = "https://example.org/iiif/1", SeeAlso = new List { - new("Dataset") - { - Id = "https://example.org/dataset/single-item.jsonld", - Format = "application/ld+json" - } + new("Dataset") + { + Id = "https://example.org/dataset/single-item.jsonld", + Format = "application/ld+json" + } } - }, - StartTime = new DateTime(2017, 9, 20, 23, 58, 0), - EndTime = new DateTime(2017, 9, 21, 0, 0, 0), - Actor = new Actor { Id = "https://example.org/person/admin1", Type = ActorType.Person } - }; - - activity.EnsureContext(Discovery.Context.ChangeDiscovery1Context); + }, + StartTime = new DateTime(2017, 9, 20, 23, 58, 0), + EndTime = new DateTime(2017, 9, 21, 0, 0, 0), + Actor = new Actor { Id = "https://example.org/person/admin1", Type = ActorType.Person } + }; + + activity.EnsureContext(Discovery.Context.ChangeDiscovery1Context); + + // Act + var json = activity.AsJson(); - // Act - var json = activity.AsJson(); - - // Assert - json.ShouldMatchJson(expected); - } + // Assert + json.ShouldMatchJson(expected); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Serialisation/ImageService2SerialiserTests.cs b/src/IIIF/IIIF.Tests/Serialisation/ImageService2SerialiserTests.cs index d467e83..bc0fc93 100644 --- a/src/IIIF/IIIF.Tests/Serialisation/ImageService2SerialiserTests.cs +++ b/src/IIIF/IIIF.Tests/Serialisation/ImageService2SerialiserTests.cs @@ -9,229 +9,228 @@ using Newtonsoft.Json.Serialization; using Xunit; -namespace IIIF.Tests.Serialisation +namespace IIIF.Tests.Serialisation; + +public class ImageService2SerialiserTests { - public class ImageService2SerialiserTests - { - private readonly JsonSerializerSettings jsonSerializerSettings; + private readonly JsonSerializerSettings jsonSerializerSettings; - public ImageService2SerialiserTests() - { - // NOTE: Using JsonSerializerSettings to facilitate testing as it makes it a LOT easier - jsonSerializerSettings = new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - ContractResolver = new CamelCasePropertyNamesContractResolver(), - Converters = new List { new ImageService2Converter() } - }; - } - - [Fact] - public void WriteJson_OutputsExpected_IfNoProfileOrProfileDescription() - { - // Arrange - var imageService = new ImageService2 { Id = "foo" }; - const string expected = "{\"@id\":\"foo\",\"@type\":\"ImageService2\",\"width\":0,\"height\":0}"; - - // Act - var result = JsonConvert.SerializeObject(imageService, jsonSerializerSettings); - - // Assert - result.Should().Be(expected); - } - - [Fact] - public void WriteJson_OutputsExpected_ProfileOnly() - { - // Arrange - var imageService = new ImageService2 { Id = "foo", Profile = "bar" }; - const string expected = - "{\"@id\":\"foo\",\"@type\":\"ImageService2\",\"profile\":\"bar\",\"width\":0,\"height\":0}"; - - // Act - var result = JsonConvert.SerializeObject(imageService, jsonSerializerSettings); - - // Assert - result.Should().Be(expected); - } - - [Fact] - public void WriteJson_OutputsExpected_ProfileDescriptionOnly() + public ImageService2SerialiserTests() + { + // NOTE: Using JsonSerializerSettings to facilitate testing as it makes it a LOT easier + jsonSerializerSettings = new JsonSerializerSettings { - // Arrange - var imageService = new ImageService2 - { - Id = "foo", - ProfileDescription = new ProfileDescription { MaxHeight = 10, MaxWidth = 20 }, - }; - const string expected = - "{\"@id\":\"foo\",\"@type\":\"ImageService2\",\"width\":0,\"height\":0,\"profile\":{\"maxHeight\":10,\"maxWidth\":20}}"; - - // Act - var result = JsonConvert.SerializeObject(imageService, jsonSerializerSettings); - - // Assert - result.Should().Be(expected); - } - - [Fact] - public void WriteJson_OutputsExpected_ProfileAndProfileDescription() + NullValueHandling = NullValueHandling.Ignore, + ContractResolver = new CamelCasePropertyNamesContractResolver(), + Converters = new List { new ImageService2Converter() } + }; + } + + [Fact] + public void WriteJson_OutputsExpected_IfNoProfileOrProfileDescription() + { + // Arrange + var imageService = new ImageService2 { Id = "foo" }; + const string expected = "{\"@id\":\"foo\",\"@type\":\"ImageService2\",\"width\":0,\"height\":0}"; + + // Act + var result = JsonConvert.SerializeObject(imageService, jsonSerializerSettings); + + // Assert + result.Should().Be(expected); + } + + [Fact] + public void WriteJson_OutputsExpected_ProfileOnly() + { + // Arrange + var imageService = new ImageService2 { Id = "foo", Profile = "bar" }; + const string expected = + "{\"@id\":\"foo\",\"@type\":\"ImageService2\",\"profile\":\"bar\",\"width\":0,\"height\":0}"; + + // Act + var result = JsonConvert.SerializeObject(imageService, jsonSerializerSettings); + + // Assert + result.Should().Be(expected); + } + + [Fact] + public void WriteJson_OutputsExpected_ProfileDescriptionOnly() + { + // Arrange + var imageService = new ImageService2 { - // Arrange - var imageService = new ImageService2 - { - Id = "foo", - Profile = "bar", - ProfileDescription = new ProfileDescription { MaxHeight = 10, MaxWidth = 20 }, - }; - const string expected = - "{\"@id\":\"foo\",\"@type\":\"ImageService2\",\"profile\":[\"bar\",{\"maxHeight\":10,\"maxWidth\":20}],\"width\":0,\"height\":0}"; - - // Act - var result = JsonConvert.SerializeObject(imageService, jsonSerializerSettings); - - // Assert - result.Should().Be(expected); - } - - [Fact] - public void ReadJson_OutputsExpected_IfNoProfileOrProfileDescription() + Id = "foo", + ProfileDescription = new ProfileDescription { MaxHeight = 10, MaxWidth = 20 } + }; + const string expected = + "{\"@id\":\"foo\",\"@type\":\"ImageService2\",\"width\":0,\"height\":0,\"profile\":{\"maxHeight\":10,\"maxWidth\":20}}"; + + // Act + var result = JsonConvert.SerializeObject(imageService, jsonSerializerSettings); + + // Assert + result.Should().Be(expected); + } + + [Fact] + public void WriteJson_OutputsExpected_ProfileAndProfileDescription() + { + // Arrange + var imageService = new ImageService2 { - // Arrange - const string json = "{\"@id\":\"foo\",\"@type\":\"ImageService2\",\"width\":0,\"height\":0}"; - var expected = new ImageService2 { Id = "foo" }; - - // Act - var result = JsonConvert.DeserializeObject(json, jsonSerializerSettings); - - // Assert - result.Should().BeEquivalentTo(expected); - } - - [Fact] - public void ReadJson_OutputsExpected_ProfileOnly() + Id = "foo", + Profile = "bar", + ProfileDescription = new ProfileDescription { MaxHeight = 10, MaxWidth = 20 } + }; + const string expected = + "{\"@id\":\"foo\",\"@type\":\"ImageService2\",\"profile\":[\"bar\",{\"maxHeight\":10,\"maxWidth\":20}],\"width\":0,\"height\":0}"; + + // Act + var result = JsonConvert.SerializeObject(imageService, jsonSerializerSettings); + + // Assert + result.Should().Be(expected); + } + + [Fact] + public void ReadJson_OutputsExpected_IfNoProfileOrProfileDescription() + { + // Arrange + const string json = "{\"@id\":\"foo\",\"@type\":\"ImageService2\",\"width\":0,\"height\":0}"; + var expected = new ImageService2 { Id = "foo" }; + + // Act + var result = JsonConvert.DeserializeObject(json, jsonSerializerSettings); + + // Assert + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public void ReadJson_OutputsExpected_ProfileOnly() + { + // Arrange + var expected = new ImageService2 { Id = "foo", Profile = "bar" }; + const string json = + "{\"@id\":\"foo\",\"@type\":\"ImageService2\",\"profile\":\"bar\",\"width\":0,\"height\":0}"; + + // Act + var result = JsonConvert.DeserializeObject(json, jsonSerializerSettings); + + // Assert + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public void ReadJson_OutputsExpected_ProfileDescriptionOnly() + { + // Arrange + var expected = new ImageService2 { - // Arrange - var expected = new ImageService2 { Id = "foo", Profile = "bar" }; - const string json = - "{\"@id\":\"foo\",\"@type\":\"ImageService2\",\"profile\":\"bar\",\"width\":0,\"height\":0}"; - - // Act - var result = JsonConvert.DeserializeObject(json, jsonSerializerSettings); - - // Assert - result.Should().BeEquivalentTo(expected); - } - - [Fact] - public void ReadJson_OutputsExpected_ProfileDescriptionOnly() + Id = "foo", + ProfileDescription = new ProfileDescription { MaxHeight = 10, MaxWidth = 20 } + }; + const string json = + "{\"@id\":\"foo\",\"@type\":\"ImageService2\",\"width\":0,\"height\":0,\"profile\":{\"maxHeight\":10,\"maxWidth\":20}}"; + + // Act + var result = JsonConvert.DeserializeObject(json, jsonSerializerSettings); + + // Assert + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public void ReadJson_OutputsExpected_ProfileAndProfileDescription() + { + // Arrange + var expected = new ImageService2 { - // Arrange - var expected = new ImageService2 - { - Id = "foo", - ProfileDescription = new ProfileDescription { MaxHeight = 10, MaxWidth = 20 }, - }; - const string json = - "{\"@id\":\"foo\",\"@type\":\"ImageService2\",\"width\":0,\"height\":0,\"profile\":{\"maxHeight\":10,\"maxWidth\":20}}"; - - // Act - var result = JsonConvert.DeserializeObject(json, jsonSerializerSettings); - - // Assert - result.Should().BeEquivalentTo(expected); - } - - [Fact] - public void ReadJson_OutputsExpected_ProfileAndProfileDescription() + Id = "foo", + Profile = "bar", + ProfileDescription = new ProfileDescription { MaxHeight = 10, MaxWidth = 20 } + }; + const string json = + "{\"@id\":\"foo\",\"@type\":\"ImageService2\",\"profile\":[\"bar\",{\"maxHeight\":10,\"maxWidth\":20}],\"width\":0,\"height\":0}"; + + // Act + var result = JsonConvert.DeserializeObject(json, jsonSerializerSettings); + + // Assert + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public void CanDeserialise_ImageService2() + { + // Arrange + var imageService = new ImageService2 { - // Arrange - var expected = new ImageService2 - { - Id = "foo", - Profile = "bar", - ProfileDescription = new ProfileDescription { MaxHeight = 10, MaxWidth = 20 }, - }; - const string json = - "{\"@id\":\"foo\",\"@type\":\"ImageService2\",\"profile\":[\"bar\",{\"maxHeight\":10,\"maxWidth\":20}],\"width\":0,\"height\":0}"; - - // Act - var result = JsonConvert.DeserializeObject(json, jsonSerializerSettings); - - // Assert - result.Should().BeEquivalentTo(expected); - } - - [Fact] - public void CanDeserialise_ImageService2() + Id = "foo", + Profile = "bar", + ProfileDescription = new ProfileDescription { MaxHeight = 10, MaxWidth = 20 } + }; + + var serialised = imageService.AsJson(); + + // Act + var deserialised = serialised.FromJson(); + + // Assert + deserialised.Should().BeEquivalentTo(imageService); + } + + [Fact] + public void CanDeserialise_ImageService_WithAuth() + { + // Arrange + var imageService = new ImageService2 { - // Arrange - var imageService = new ImageService2 + Id = "foo", + Profile = "bar", + ProfileDescription = new ProfileDescription { MaxHeight = 10, MaxWidth = 20 }, + Sizes = new List { - Id = "foo", - Profile = "bar", - ProfileDescription = new ProfileDescription { MaxHeight = 10, MaxWidth = 20 }, - }; - - var serialised = imageService.AsJson(); - - // Act - var deserialised = serialised.FromJson(); - - // Assert - deserialised.Should().BeEquivalentTo(imageService); - } - - [Fact] - public void CanDeserialise_ImageService_WithAuth() - { - // Arrange - var imageService = new ImageService2 + new(100, 200), + new(400, 800) + }, + Tiles = new List { - Id = "foo", - Profile = "bar", - ProfileDescription = new ProfileDescription { MaxHeight = 10, MaxWidth = 20 }, - Sizes = new List - { - new(100, 200), - new(400, 800), - }, - Tiles = new List + new() { - new() - { - Height = 512, Width = 512, ScaleFactors = new[] { 1, 2, 4 } - } - }, - Service = new List + Height = 512, Width = 512, ScaleFactors = new[] { 1, 2, 4 } + } + }, + Service = new List + { + new AuthCookieService(AuthCookieService.ClickthroughProfile) { - new AuthCookieService(AuthCookieService.ClickthroughProfile) + Id = "http://clickthrough", + Label = new MetaDataValue("This is the label"), + Description = new MetaDataValue("This is the description"), + Service = new List { - Id = "http://clickthrough", - Label = new MetaDataValue("This is the label"), - Description = new MetaDataValue("This is the description"), - Service = new List + new AuthLogoutService { - new AuthLogoutService - { - Id = "http://clickthrough/logout", - }, - new AuthTokenService - { - Id = "http://clickthrough/token", - } + Id = "http://clickthrough/logout" + }, + new AuthTokenService + { + Id = "http://clickthrough/token" } } } - }; - - var serialised = imageService.AsJson(); - - // Act - var deserialised = serialised.FromJson(); - - // Assert - deserialised.Should().BeEquivalentTo(imageService); - } + } + }; + + var serialised = imageService.AsJson(); + + // Act + var deserialised = serialised.FromJson(); + + // Assert + deserialised.Should().BeEquivalentTo(imageService); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Serialisation/ImageService3SerialiserTests.cs b/src/IIIF/IIIF.Tests/Serialisation/ImageService3SerialiserTests.cs index 137fc4c..152cd2a 100644 --- a/src/IIIF/IIIF.Tests/Serialisation/ImageService3SerialiserTests.cs +++ b/src/IIIF/IIIF.Tests/Serialisation/ImageService3SerialiserTests.cs @@ -6,155 +6,154 @@ using IIIF.Serialisation; using Xunit; -namespace IIIF.Tests.Serialisation +namespace IIIF.Tests.Serialisation; + +public class ImageService3SerialiserTests { - public class ImageService3SerialiserTests + [Fact] + public void WriteJson_OutputsExpected_IfNoProfile() { - [Fact] - public void WriteJson_OutputsExpected_IfNoProfile() - { - // Arrange - var imageService = new ImageService3 { Id = "foo" }; - const string expected = "{\n \"id\": \"foo\",\n \"type\": \"ImageService3\"\n}"; - - // Act - var result = imageService.AsJson().Replace("\r\n", "\n"); - - // Assert - result.Should().Be(expected); - } - - [Fact] - public void WriteJson_OutputsExpected_Profile() - { - // Arrange - var imageService = new ImageService3 { Id = "foo", Profile = "bar" }; - const string expected = "{\n \"id\": \"foo\",\n \"type\": \"ImageService3\",\n \"profile\": \"bar\"\n}"; - - // Act - var result = imageService.AsJson().Replace("\r\n", "\n"); - - // Assert - result.Should().Be(expected); - } - - [Fact] - public void CanDeserialise_ImageService3() + // Arrange + var imageService = new ImageService3 { Id = "foo" }; + const string expected = "{\n \"id\": \"foo\",\n \"type\": \"ImageService3\"\n}"; + + // Act + var result = imageService.AsJson().Replace("\r\n", "\n"); + + // Assert + result.Should().Be(expected); + } + + [Fact] + public void WriteJson_OutputsExpected_Profile() + { + // Arrange + var imageService = new ImageService3 { Id = "foo", Profile = "bar" }; + const string expected = "{\n \"id\": \"foo\",\n \"type\": \"ImageService3\",\n \"profile\": \"bar\"\n}"; + + // Act + var result = imageService.AsJson().Replace("\r\n", "\n"); + + // Assert + result.Should().Be(expected); + } + + [Fact] + public void CanDeserialise_ImageService3() + { + // Arrange + var imageService = new ImageService3 { - // Arrange - var imageService = new ImageService3 - { - Id = "foo", - Profile = "level1", - Height = 1234, - Width = 2000, - ExtraFeatures = new List { Features.Cors, Features.RegionSquare }, - PreferredFormats = new List { "webp", "png" }, - ExtraQualities = new List { "bitonal", "gray" }, - ExtraFormats = new List { "jpg" }, - Rights = "http://rightsstatements.org/vocab/InC-EDU/1.0/", - }; - - var serialised = imageService.AsJson(); - - // Act - var deserialised = serialised.FromJson(); - - // Assert - deserialised.Should().BeEquivalentTo(imageService); - } - - [Fact] - public void CanDeserialise_ImageService3_WithAuth() + Id = "foo", + Profile = "level1", + Height = 1234, + Width = 2000, + ExtraFeatures = new List { Features.Cors, Features.RegionSquare }, + PreferredFormats = new List { "webp", "png" }, + ExtraQualities = new List { "bitonal", "gray" }, + ExtraFormats = new List { "jpg" }, + Rights = "http://rightsstatements.org/vocab/InC-EDU/1.0/" + }; + + var serialised = imageService.AsJson(); + + // Act + var deserialised = serialised.FromJson(); + + // Assert + deserialised.Should().BeEquivalentTo(imageService); + } + + [Fact] + public void CanDeserialise_ImageService3_WithAuth() + { + // Arrange + var imageService = new ImageService3 { - // Arrange - var imageService = new ImageService3 + Id = "foo", + Profile = "level1", + Height = 1234, + Width = 2000, + ExtraFeatures = new List { Features.Cors, Features.RegionSquare }, + PreferredFormats = new List { "webp", "png" }, + ExtraQualities = new List { "bitonal", "gray" }, + ExtraFormats = new List { "jpg" }, + Rights = "http://rightsstatements.org/vocab/InC-EDU/1.0/", + Service = new List { - Id = "foo", - Profile = "level1", - Height = 1234, - Width = 2000, - ExtraFeatures = new List { Features.Cors, Features.RegionSquare }, - PreferredFormats = new List { "webp", "png" }, - ExtraQualities = new List { "bitonal", "gray" }, - ExtraFormats = new List { "jpg" }, - Rights = "http://rightsstatements.org/vocab/InC-EDU/1.0/", - Service = new List + new AuthCookieService(AuthCookieService.ClickthroughProfile) { - new AuthCookieService(AuthCookieService.ClickthroughProfile) + Id = "http://clickthrough", + Label = new MetaDataValue("This is the label"), + Description = new MetaDataValue("This is the description"), + Service = new List { - Id = "http://clickthrough", - Label = new MetaDataValue("This is the label"), - Description = new MetaDataValue("This is the description"), - Service = new List + new AuthLogoutService { - new AuthLogoutService - { - Id = "http://clickthrough/logout", - }, - new AuthTokenService - { - Id = "http://clickthrough/token", - } + Id = "http://clickthrough/logout" + }, + new AuthTokenService + { + Id = "http://clickthrough/token" } } } - }; - - var serialised = imageService.AsJson(); - - // Act - var deserialised = serialised.FromJson(); - - // Assert - deserialised.Should().BeEquivalentTo(imageService); - } - - [Fact] - public void CanDeserialise_ImageService3_WithAuthV0() + } + }; + + var serialised = imageService.AsJson(); + + // Act + var deserialised = serialised.FromJson(); + + // Assert + deserialised.Should().BeEquivalentTo(imageService); + } + + [Fact] + public void CanDeserialise_ImageService3_WithAuthV0() + { + // Arrange + var imageService = new ImageService3 { - // Arrange - var imageService = new ImageService3 + Id = "foo", + Profile = "level1", + Height = 1234, + Width = 2000, + ExtraFeatures = new List { Features.Cors, Features.RegionSquare }, + PreferredFormats = new List { "webp", "png" }, + ExtraQualities = new List { "bitonal", "gray" }, + ExtraFormats = new List { "jpg" }, + Rights = "http://rightsstatements.org/vocab/InC-EDU/1.0/", + Service = new List { - Id = "foo", - Profile = "level1", - Height = 1234, - Width = 2000, - ExtraFeatures = new List { Features.Cors, Features.RegionSquare }, - PreferredFormats = new List { "webp", "png" }, - ExtraQualities = new List { "bitonal", "gray" }, - ExtraFormats = new List { "jpg" }, - Rights = "http://rightsstatements.org/vocab/InC-EDU/1.0/", - Service = new List + new IIIF.Auth.V0.AuthCookieService(IIIF.Auth.V0.AuthCookieService.ClickthroughProfile) { - new IIIF.Auth.V0.AuthCookieService(IIIF.Auth.V0.AuthCookieService.ClickthroughProfile) + Id = "http://clickthrough", + Label = new MetaDataValue("This is the label"), + Description = new MetaDataValue("This is the description"), + Service = new List { - Id = "http://clickthrough", - Label = new MetaDataValue("This is the label"), - Description = new MetaDataValue("This is the description"), - Service = new List + new IIIF.Auth.V0.AuthLogoutService + { + Id = "http://clickthrough/logout", + Description = new MetaDataValue("Logout description") + }, + new IIIF.Auth.V0.AuthTokenService { - new IIIF.Auth.V0.AuthLogoutService - { - Id = "http://clickthrough/logout", - Description = new MetaDataValue("Logout description"), - }, - new IIIF.Auth.V0.AuthTokenService - { - Id = "http://clickthrough/token", - } + Id = "http://clickthrough/token" } } } - }; - - var serialised = imageService.AsJson(); - - // Act - var deserialised = serialised.FromJson(); - - // Assert - deserialised.Should().BeEquivalentTo(imageService); - } + } + }; + + var serialised = imageService.AsJson(); + + // Act + var deserialised = serialised.FromJson(); + + // Assert + deserialised.Should().BeEquivalentTo(imageService); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Serialisation/LanguageMapSerialiserTests.cs b/src/IIIF/IIIF.Tests/Serialisation/LanguageMapSerialiserTests.cs index c96a119..f2a428d 100644 --- a/src/IIIF/IIIF.Tests/Serialisation/LanguageMapSerialiserTests.cs +++ b/src/IIIF/IIIF.Tests/Serialisation/LanguageMapSerialiserTests.cs @@ -6,74 +6,74 @@ using Newtonsoft.Json; using Xunit; -namespace IIIF.Tests.Serialisation +namespace IIIF.Tests.Serialisation; + +public class LanguageMapSerialiserTests { - public class LanguageMapSerialiserTests + private readonly LanguageMapSerialiser sut; + + public LanguageMapSerialiserTests() + { + sut = new LanguageMapSerialiser(); + } + + [Fact] + public void WriteJson_WritesSingleLine_IfSingleLanguageWithSingleShortValue() + { + // Arrange + var languageMap = new LanguageMap("en", "single and short"); + var expected = "{\"en\":[\"single and short\"]}"; + + // Act + var result = JsonConvert.SerializeObject(languageMap, Formatting.Indented, sut); + + // Assert + result.Should().Be(expected); + } + + [Fact] + public void WriteJson_WritesIndented_IfSingleLanguageWithSingleLongValue() + { + // Arrange + var languageMap = new LanguageMap("en", "this string is slightly longer than the threshold"); + var expected = + $"{{{Environment.NewLine} \"en\": [{Environment.NewLine} \"this string is slightly longer than the threshold\"{Environment.NewLine} ]{Environment.NewLine}}}"; + + // Act + var result = JsonConvert.SerializeObject(languageMap, Formatting.Indented, sut); + + // Assert + result.Should().Be(expected); + } + + [Fact] + public void WriteJson_WritesIndented_IfSingleLanguageWithMultipleShortValues() { - private readonly LanguageMapSerialiser sut; - public LanguageMapSerialiserTests() - { - sut = new LanguageMapSerialiser(); - } - - [Fact] - public void WriteJson_WritesSingleLine_IfSingleLanguageWithSingleShortValue() - { - // Arrange - var languageMap = new LanguageMap("en", "single and short"); - var expected = "{\"en\":[\"single and short\"]}"; - - // Act - var result = JsonConvert.SerializeObject(languageMap, Formatting.Indented, sut); - - // Assert - result.Should().Be(expected); - } - - [Fact] - public void WriteJson_WritesIndented_IfSingleLanguageWithSingleLongValue() - { - // Arrange - var languageMap = new LanguageMap("en", "this string is slightly longer than the threshold"); - var expected = - $"{{{Environment.NewLine} \"en\": [{Environment.NewLine} \"this string is slightly longer than the threshold\"{Environment.NewLine} ]{Environment.NewLine}}}"; - - // Act - var result = JsonConvert.SerializeObject(languageMap, Formatting.Indented, sut); - - // Assert - result.Should().Be(expected); - } - - [Fact] - public void WriteJson_WritesIndented_IfSingleLanguageWithMultipleShortValues() - { - // Arrange - var languageMap = new LanguageMap("en", new[] {"single and short", "also short"}); - var expected = - $"{{{Environment.NewLine} \"en\": [{Environment.NewLine} \"single and short\",{Environment.NewLine} \"also short\"{Environment.NewLine} ]{Environment.NewLine}}}"; - - // Act - var result = JsonConvert.SerializeObject(languageMap, Formatting.Indented, sut); - - // Assert - result.Should().Be(expected); - } - - [Fact] - public void WriteJson_WritesIndented_IfMultiLanguageWithShortValues() - { - // Arrange - var languageMap = new LanguageMap("en", "single and short"); - languageMap.Add("fr", new List {"also short"}); - var expected = - $"{{{Environment.NewLine} \"en\": [{Environment.NewLine} \"single and short\"{Environment.NewLine} ],{Environment.NewLine} \"fr\": [{Environment.NewLine} \"also short\"{Environment.NewLine} ]{Environment.NewLine}}}"; - - // Act - var result = JsonConvert.SerializeObject(languageMap, Formatting.Indented, sut); - - // Assert - result.Should().Be(expected); - } + // Arrange + var languageMap = new LanguageMap("en", new[] { "single and short", "also short" }); + var expected = + $"{{{Environment.NewLine} \"en\": [{Environment.NewLine} \"single and short\",{Environment.NewLine} \"also short\"{Environment.NewLine} ]{Environment.NewLine}}}"; + + // Act + var result = JsonConvert.SerializeObject(languageMap, Formatting.Indented, sut); + + // Assert + result.Should().Be(expected); + } + + [Fact] + public void WriteJson_WritesIndented_IfMultiLanguageWithShortValues() + { + // Arrange + var languageMap = new LanguageMap("en", "single and short"); + languageMap.Add("fr", new List { "also short" }); + var expected = + $"{{{Environment.NewLine} \"en\": [{Environment.NewLine} \"single and short\"{Environment.NewLine} ],{Environment.NewLine} \"fr\": [{Environment.NewLine} \"also short\"{Environment.NewLine} ]{Environment.NewLine}}}"; + + // Act + var result = JsonConvert.SerializeObject(languageMap, Formatting.Indented, sut); + + // Assert + result.Should().Be(expected); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Serialisation/ManifestSerialisationTests.cs b/src/IIIF/IIIF.Tests/Serialisation/ManifestSerialisationTests.cs index 2ab8ad8..4823d0a 100644 --- a/src/IIIF/IIIF.Tests/Serialisation/ManifestSerialisationTests.cs +++ b/src/IIIF/IIIF.Tests/Serialisation/ManifestSerialisationTests.cs @@ -11,181 +11,180 @@ using Xunit; using ExternalResource = IIIF.Presentation.V3.Content.ExternalResource; -namespace IIIF.Tests.Serialisation +namespace IIIF.Tests.Serialisation; + +public class ManifestSerialisationTests { - public class ManifestSerialisationTests - { - private Manifest sampleManifest; + private Manifest sampleManifest; - public ManifestSerialisationTests() + public ManifestSerialisationTests() + { + sampleManifest = new Manifest { - sampleManifest = new Manifest + Context = "http://iiif.io/api/presentation/3/context.json", + Id = "https://test.example.com/manifest", + Label = new LanguageMap("en", "Test string"), + Thumbnail = new List { - Context = "http://iiif.io/api/presentation/3/context.json", - Id = "https://test.example.com/manifest", - Label = new LanguageMap("en", "Test string"), - Thumbnail = new List + new Image { - new Image + Id = "https://test.image", + Format = "image/jpeg", + Service = new List { - Id = "https://test.image", - Format = "image/jpeg", - Service = new List + new ImageService2 { - new ImageService2 - { - Id = "https://test.example.com/canvas/1/image", - Profile = ImageService2.Level1Profile, - Context = ImageService2.Image2Context, - Width = 200, - Height = 200 - } - }, + Id = "https://test.example.com/canvas/1/image", + Profile = ImageService2.Level1Profile, + Context = ImageService2.Image2Context, + Width = 200, + Height = 200 + } } - }, - Items = new List + } + }, + Items = new List + { + new() { - new() + Id = "https://test.example.com/canvas/1", + Width = 1000, + Height = 1001, + Items = new List { - Id = "https://test.example.com/canvas/1", - Width = 1000, - Height = 1001, - Items = new List + new() { - new() + Id = "https://test.example.com/canvas/1/page", + Items = new List { - Id = "https://test.example.com/canvas/1/page", - Items = new List + new PaintingAnnotation { - new PaintingAnnotation + Target = new Canvas { Id = "https://test.example.com/canvas/1" }, + Id = "https://test.example.com/canvas/1/page/image", + Body = new Image { - Target = new Canvas { Id = "https://test.example.com/canvas/1" }, - Id = "https://test.example.com/canvas/1/page/image", - Body = new Image + Id = + "https://test.example.com/canvas/1/image/full/max/1000,1000/0/default.jpg", + Format = "image/jpeg", + Service = new List { - Id = - "https://test.example.com/canvas/1/image/full/max/1000,1000/0/default.jpg", - Format = "image/jpeg", - Service = new List + new ImageService2 { - new ImageService2 - { - Id = "https://test.example.com/canvas/1/image", - Profile = ImageService2.Level1Profile, - Context = ImageService2.Image2Context, - Width = 1000, - Height = 1001 - }, - new ImageService3 - { - Id = "https://test.example.com/canvas/1/image/3", - Profile = ImageService3.Level2Profile, - Context = ImageService3.Image3Context, - Width = 1000, - Height = 1001 - } + Id = "https://test.example.com/canvas/1/image", + Profile = ImageService2.Level1Profile, + Context = ImageService2.Image2Context, + Width = 1000, + Height = 1001 }, + new ImageService3 + { + Id = "https://test.example.com/canvas/1/image/3", + Profile = ImageService3.Level2Profile, + Context = ImageService3.Image3Context, + Width = 1000, + Height = 1001 + } } } } } - }, - Thumbnail = new List + } + }, + Thumbnail = new List + { + new Image { - new Image + Id = "https://test.image", + Format = "image/jpeg", + Service = new List { - Id = "https://test.image", - Format = "image/jpeg", - Service = new List + new ImageService2 { - new ImageService2 - { - Id = "https://test.example.com/canvas/1/image", - Profile = ImageService2.Level1Profile, - Context = ImageService2.Image2Context, - Width = 200, - Height = 200 - } - }, + Id = "https://test.example.com/canvas/1/image", + Profile = ImageService2.Level1Profile, + Context = ImageService2.Image2Context, + Width = 200, + Height = 200 + } } } } - }, - Homepage = new List - { - new("Text") - { - Id = "https://test.example.com/homepage", - Label = new LanguageMap("en", "My Homepage"), - Format = "text/html", - Language = new List { "en" } - }, - }, - Metadata = new List + } + }, + Homepage = new List + { + new("Text") { - new("en", "Gibberish", "foo", "bar"), - new("en", "Published", "December 2021"), - }, - Rights = "https://en.wikipedia.org/wiki/All_rights_reserved", - Provider = new List + Id = "https://test.example.com/homepage", + Label = new LanguageMap("en", "My Homepage"), + Format = "text/html", + Language = new List { "en" } + } + }, + Metadata = new List + { + new("en", "Gibberish", "foo", "bar"), + new("en", "Published", "December 2021") + }, + Rights = "https://en.wikipedia.org/wiki/All_rights_reserved", + Provider = new List + { + new() { - new() + Id = "https://test.example.com", + Label = new LanguageMap("en", new[] { "one", "two" }), + Homepage = new List { - Id = "https://test.example.com", - Label = new LanguageMap("en", new[] { "one", "two" }), - Homepage = new List - { - new("Text") - { - Id = "https://test.example.com/homepage", - Label = new LanguageMap("en", "My Homepage"), - Format = "text/html", - Language = new List { "en" } - }, - }, - Logo = new List + new("Text") { - new() - { - Id = "https://test.example.com/logo", - Format = "image/jpeg", - } + Id = "https://test.example.com/homepage", + Label = new LanguageMap("en", "My Homepage"), + Format = "text/html", + Language = new List { "en" } } }, - }, - SeeAlso = new List - { - new("Dataset") + Logo = new List { - Id = "https://test.example.com/other", - Profile = "https://api.test.example.com/context.json", - Label = new LanguageMap("en", "API Stuff"), - Format = "application/json", - }, + new() + { + Id = "https://test.example.com/logo", + Format = "image/jpeg" + } + } } - }; - } + }, + SeeAlso = new List + { + new("Dataset") + { + Id = "https://test.example.com/other", + Profile = "https://api.test.example.com/context.json", + Label = new LanguageMap("en", "API Stuff"), + Format = "application/json" + } + } + }; + } - [Fact] - public void CanDeserialiseSerialisedManifest() - { - var serialisedManifest = sampleManifest.AsJson(); + [Fact] + public void CanDeserialiseSerialisedManifest() + { + var serialisedManifest = sampleManifest.AsJson(); - var deserialised = serialisedManifest.FromJson(); + var deserialised = serialisedManifest.FromJson(); - deserialised.Should().BeEquivalentTo(sampleManifest); - } - - [Fact] - public void CanDeserialiseSerialisedManifest_Stream() - { - using var memoryStream = new MemoryStream(); - sampleManifest.AsJsonStream(memoryStream); + deserialised.Should().BeEquivalentTo(sampleManifest); + } + + [Fact] + public void CanDeserialiseSerialisedManifest_Stream() + { + using var memoryStream = new MemoryStream(); + sampleManifest.AsJsonStream(memoryStream); - memoryStream.Position = 0; - var deserialised = memoryStream.FromJsonStream(); + memoryStream.Position = 0; + var deserialised = memoryStream.FromJsonStream(); - deserialised.Should().BeEquivalentTo(sampleManifest); - } + deserialised.Should().BeEquivalentTo(sampleManifest); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Serialisation/MixedAuthServicesTest.cs b/src/IIIF/IIIF.Tests/Serialisation/MixedAuthServicesTest.cs index 743ec0d..9d5ed8a 100644 --- a/src/IIIF/IIIF.Tests/Serialisation/MixedAuthServicesTest.cs +++ b/src/IIIF/IIIF.Tests/Serialisation/MixedAuthServicesTest.cs @@ -7,45 +7,45 @@ using IIIF.Tests.Auth.V2; using Xunit; -namespace IIIF.Tests.Serialisation +namespace IIIF.Tests.Serialisation; + +public class MixedAuthServicesTest { - public class MixedAuthServicesTest + [Fact] + public void Image2_Can_Have_V1_And_V2_Auth_Services() { - [Fact] - public void Image2_Can_Have_V1_And_V2_Auth_Services() + // Arrange + var auth1CookieService = AuthCookieService.NewClickthroughInstance(); + auth1CookieService.Id = "https://example.com/login"; + auth1CookieService.Label = new MetaDataValue("auth1 - label"); + auth1CookieService.Header = new MetaDataValue("auth1 - header"); + auth1CookieService.Description = new MetaDataValue("auth1 - desc"); + auth1CookieService.ConfirmLabel = new MetaDataValue("auth1 - confirm"); + auth1CookieService.FailureHeader = new MetaDataValue("auth1 - fail"); + auth1CookieService.FailureDescription = new MetaDataValue("auth1 - fail-desc"); + auth1CookieService.Service = new List { - // Arrange - var auth1CookieService = AuthCookieService.NewClickthroughInstance(); - auth1CookieService.Id = "https://example.com/login"; - auth1CookieService.Label = new MetaDataValue("auth1 - label"); - auth1CookieService.Header = new MetaDataValue("auth1 - header"); - auth1CookieService.Description = new MetaDataValue("auth1 - desc"); - auth1CookieService.ConfirmLabel = new MetaDataValue("auth1 - confirm"); - auth1CookieService.FailureHeader = new MetaDataValue("auth1 - fail"); - auth1CookieService.FailureDescription = new MetaDataValue("auth1 - fail-desc"); - auth1CookieService.Service = new List + new AuthTokenService { - new AuthTokenService - { - Id = "https://example.com/token" - }, - new AuthLogoutService - { - Id = "https://example.com/logout", - Label = new MetaDataValue("Log out") - } - }; - var imgService2 = new ImageService2 + Id = "https://example.com/token" + }, + new AuthLogoutService { - Id = "https://example.org/images/my-image.jpg/v2/service", - Service = new List { auth1CookieService } - }; - - imgService2.Service.AddRange(ReusableParts.Auth2Services); - - // Act - var json = imgService2.AsJson().Replace("\r\n", "\n"); - const string expected = @"{ + Id = "https://example.com/logout", + Label = new MetaDataValue("Log out") + } + }; + var imgService2 = new ImageService2 + { + Id = "https://example.org/images/my-image.jpg/v2/service", + Service = new List { auth1CookieService } + }; + + imgService2.Service.AddRange(ReusableParts.Auth2Services); + + // Act + var json = imgService2.AsJson().Replace("\r\n", "\n"); + const string expected = @"{ ""@id"": ""https://example.org/images/my-image.jpg/v2/service"", ""@type"": ""ImageService2"", ""service"": [ @@ -101,10 +101,8 @@ public void Image2_Can_Have_V1_And_V2_Auth_Services() } ] }"; - - // Assert - json.Should().BeEquivalentTo(expected); - - } + + // Assert + json.Should().BeEquivalentTo(expected); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Serialisation/MixedVersionSerialisertests.cs b/src/IIIF/IIIF.Tests/Serialisation/MixedVersionSerialisertests.cs index 53db3dc..5ea45fb 100644 --- a/src/IIIF/IIIF.Tests/Serialisation/MixedVersionSerialisertests.cs +++ b/src/IIIF/IIIF.Tests/Serialisation/MixedVersionSerialisertests.cs @@ -6,36 +6,37 @@ using IIIF.Serialisation; using Xunit; -namespace IIIF.Tests.Serialisation +namespace IIIF.Tests.Serialisation; + +public class MixedVersionSerialiserTests { - public class MixedVersionSerialiserTests + [Fact] + public void Image_Can_Have_V2_And_V3_Image_Services() { - [Fact] - public void Image_Can_Have_V2_And_V3_Image_Services() + // Arrange + var img = new Image { - // Arrange - var img = new Image + Id = $"https://example.org/images/my-image.jpg", + Width = 1000, + Height = 1000, + Format = "image/jpeg", + Service = new List { - Id = $"https://example.org/images/my-image.jpg", - Width = 1000, - Height = 1000, - Format = "image/jpeg", - Service = new List + new ImageService2 + { + Id = "https://example.org/images/my-image.jpg/v2/service" + }, + new ImageService3 { - new ImageService2 - { - Id = "https://example.org/images/my-image.jpg/v2/service" - }, - new ImageService3 - { - Id = "https://example.org/images/my-image.jpg/v2/service" - } + Id = "https://example.org/images/my-image.jpg/v2/service" } - }; - - // Act - var json = img.AsJson().Replace("\r\n", "\n");; - const string expected = @"{ + } + }; + + // Act + var json = img.AsJson().Replace("\r\n", "\n"); + ; + const string expected = @"{ ""id"": ""https://example.org/images/my-image.jpg"", ""type"": ""Image"", ""width"": 1000, @@ -52,11 +53,7 @@ public void Image_Can_Have_V2_And_V3_Image_Services() } ] }"; - // Assert - json.Should().BeEquivalentTo(expected); - - } - - + // Assert + json.Should().BeEquivalentTo(expected); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Serialisation/ObjectIfSingleConverterTests.cs b/src/IIIF/IIIF.Tests/Serialisation/ObjectIfSingleConverterTests.cs index 47c252a..999cf44 100644 --- a/src/IIIF/IIIF.Tests/Serialisation/ObjectIfSingleConverterTests.cs +++ b/src/IIIF/IIIF.Tests/Serialisation/ObjectIfSingleConverterTests.cs @@ -9,40 +9,40 @@ namespace IIIF.Tests.Serialisation; public class ObjectIfSingleConverterTests { private readonly ObjectIfSingleConverter sut; - + public ObjectIfSingleConverterTests() { sut = new ObjectIfSingleConverter(); } - + [Fact] public void ReadJson_Single_String() { // Arrange const string value = "\"Foo bar\""; var expected = new List { "Foo bar" }; - + // Act var result = JsonConvert.DeserializeObject>(value, sut); - + // Assert result.Should().BeEquivalentTo(expected); } - + [Fact] public void ReadJson_Array_String() { // Arrange const string value = "[\"Foo\",\"bar\"]"; var expected = new List { "Foo", "bar" }; - + // Act var result = JsonConvert.DeserializeObject>(value, sut); - + // Assert result.Should().BeEquivalentTo(expected); } - + [Fact] public void ReadJson_Single_ComplexType() { @@ -50,11 +50,11 @@ public void ReadJson_Single_ComplexType() var value = new ComplexType { Age = 50, Name = "grogu" }; var json = JsonConvert.SerializeObject(value); var expected = new List { value }; - + // Act // Act var result = JsonConvert.DeserializeObject>(json, sut); - + // Assert result.Should().BeEquivalentTo(expected); } @@ -69,11 +69,11 @@ public void ReadJson_Array_ComplexType() new() { Age = 38, Name = "bo-katan" } }; var json = JsonConvert.SerializeObject(expected); - + // Act // Act var result = JsonConvert.DeserializeObject>(json, sut); - + // Assert result.Should().BeEquivalentTo(expected); } @@ -83,4 +83,4 @@ public class ComplexType { public string Name { get; set; } public int Age { get; set; } -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Serialisation/PrettyIIIFContractResolverTests.cs b/src/IIIF/IIIF.Tests/Serialisation/PrettyIIIFContractResolverTests.cs index f2cbcd7..2e848c4 100644 --- a/src/IIIF/IIIF.Tests/Serialisation/PrettyIIIFContractResolverTests.cs +++ b/src/IIIF/IIIF.Tests/Serialisation/PrettyIIIFContractResolverTests.cs @@ -4,191 +4,188 @@ using Newtonsoft.Json; using Xunit; -namespace IIIF.Tests.Serialisation +namespace IIIF.Tests.Serialisation; + +public class PrettyIIIFContractResolverTests { - public class PrettyIIIFContractResolverTests - { - private readonly JsonSerializerSettings jsonSerializerSettings; + private readonly JsonSerializerSettings jsonSerializerSettings; - public PrettyIIIFContractResolverTests() - { - // NOTE: Using JsonSerializerSettings to facilitate testing as it makes it a LOT easier - jsonSerializerSettings = new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - ContractResolver = new PrettyIIIFContractResolver(), - }; - } - - [Theory] - [InlineData(0, 123, "{\"height\":123}")] - [InlineData(123, 0, "{\"width\":123}")] - [InlineData(99, 123, "{\"width\":99,\"height\":123}")] - [InlineData(0, 0, "{}")] - public void WidthHeightIgnored_IfZero(int width, int height, string expected) - { - // Arrange - var testClass = new TestClass {Width = width, Height = height}; - - // Act - var output = JsonConvert.SerializeObject(testClass, jsonSerializerSettings); - - // Assert - output.Should().Be(expected); - } - - [Theory] - [InlineData(0, 123, "{\"height\":123}")] - [InlineData(null, 123, "{\"height\":123}")] - [InlineData(123, 0, "{\"width\":123}")] - [InlineData(123, null, "{\"width\":123}")] - [InlineData(99, 123, "{\"width\":99,\"height\":123}")] - [InlineData(0, 0, "{}")] - [InlineData(null, null, "{}")] - public void NullableWidthHeightIgnored_IfZeroOrNull(int? width, int? height, string expected) - { - // Arrange - var testClass = new NullableTestClass {Width = width, Height = height}; - - // Act - var output = JsonConvert.SerializeObject(testClass, jsonSerializerSettings); - - // Assert - output.Should().Be(expected); - } - - [Fact] - public void NullList_NotSerialised() - { - // Arrange - var testClass = new TestClass {OptionalList = null}; - const string expected = "{}"; - - // Act - var output = JsonConvert.SerializeObject(testClass, jsonSerializerSettings); - - // Assert - output.Should().Be(expected); - } - - [Fact] - public void EmptyList_NotSerialised() - { - // Arrange - var testClass = new TestClass {OptionalList = new List()}; - const string expected = "{}"; - - // Act - var output = JsonConvert.SerializeObject(testClass, jsonSerializerSettings); - - // Assert - output.Should().Be(expected); - } - - [Fact] - public void PopulatedList_Serialised() - { - // Arrange - var testClass = new TestClass {OptionalList = new List{"foo"}}; - const string expected = "{\"optionalList\":[\"foo\"]}"; - - // Act - var output = JsonConvert.SerializeObject(testClass, jsonSerializerSettings); - - // Assert - output.Should().Be(expected); - } - - [Fact] - public void NullList_NotSerialised_IfMarkedAsRequired() - { - // Arrange - var testClass = new TestClass {RequiredList = null}; - const string expected = "{}"; - - // Act - var output = JsonConvert.SerializeObject(testClass, jsonSerializerSettings); - - // Assert - output.Should().Be(expected); - } - - [Fact] - public void EmptyList_Serialised_IfMarkedAsRequired() - { - // Arrange - var testClass = new TestClass {RequiredList = new List()}; - const string expected = "{\"requiredList\":[]}"; - - // Act - var output = JsonConvert.SerializeObject(testClass, jsonSerializerSettings); - - // Assert - output.Should().Be(expected); - } - - [Fact] - public void PopulatedList_Serialised_IfMarkedAsRequired() - { - // Arrange - var testClass = new TestClass {RequiredList = new List{"foo"}}; - const string expected = "{\"requiredList\":[\"foo\"]}"; - - // Act - var output = JsonConvert.SerializeObject(testClass, jsonSerializerSettings); - - // Assert - output.Should().Be(expected); - } - - [Fact] - public void List_SerialisedAsObject_IfSingleElement_AndMarkedAsObjectIfSingle() - { - // Arrange - var testClass = new TestClass {OneOrMore = new List{"foo"}}; - const string expected = "{\"oneOrMore\":\"foo\"}"; - - // Act - var output = JsonConvert.SerializeObject(testClass, jsonSerializerSettings); - - // Assert - output.Should().Be(expected); - } - - [Fact] - public void List_SerialisedAsArray_IfMultipleElements_AndMarkedAsObjectIfSingle() - { - // Arrange - var testClass = new TestClass {OneOrMore = new List{"foo", "bar"}}; - const string expected = "{\"oneOrMore\":[\"foo\",\"bar\"]}"; - - // Act - var output = JsonConvert.SerializeObject(testClass, jsonSerializerSettings); - - // Assert - output.Should().Be(expected); - } - - private class TestClass - { - public List OptionalList { get; set; } - - [RequiredOutput] - public List RequiredList { get; set; } - - [ObjectIfSingle] - public List OneOrMore { get; set; } - - public int Width { get; set; } - - public int Height { get; set; } - } - - private class NullableTestClass + public PrettyIIIFContractResolverTests() + { + // NOTE: Using JsonSerializerSettings to facilitate testing as it makes it a LOT easier + jsonSerializerSettings = new JsonSerializerSettings { - public int? Width { get; set; } - - public int? Height { get; set; } - } + NullValueHandling = NullValueHandling.Ignore, + ContractResolver = new PrettyIIIFContractResolver() + }; + } + + [Theory] + [InlineData(0, 123, "{\"height\":123}")] + [InlineData(123, 0, "{\"width\":123}")] + [InlineData(99, 123, "{\"width\":99,\"height\":123}")] + [InlineData(0, 0, "{}")] + public void WidthHeightIgnored_IfZero(int width, int height, string expected) + { + // Arrange + var testClass = new TestClass { Width = width, Height = height }; + + // Act + var output = JsonConvert.SerializeObject(testClass, jsonSerializerSettings); + + // Assert + output.Should().Be(expected); + } + + [Theory] + [InlineData(0, 123, "{\"height\":123}")] + [InlineData(null, 123, "{\"height\":123}")] + [InlineData(123, 0, "{\"width\":123}")] + [InlineData(123, null, "{\"width\":123}")] + [InlineData(99, 123, "{\"width\":99,\"height\":123}")] + [InlineData(0, 0, "{}")] + [InlineData(null, null, "{}")] + public void NullableWidthHeightIgnored_IfZeroOrNull(int? width, int? height, string expected) + { + // Arrange + var testClass = new NullableTestClass { Width = width, Height = height }; + + // Act + var output = JsonConvert.SerializeObject(testClass, jsonSerializerSettings); + + // Assert + output.Should().Be(expected); + } + + [Fact] + public void NullList_NotSerialised() + { + // Arrange + var testClass = new TestClass { OptionalList = null }; + const string expected = "{}"; + + // Act + var output = JsonConvert.SerializeObject(testClass, jsonSerializerSettings); + + // Assert + output.Should().Be(expected); + } + + [Fact] + public void EmptyList_NotSerialised() + { + // Arrange + var testClass = new TestClass { OptionalList = new List() }; + const string expected = "{}"; + + // Act + var output = JsonConvert.SerializeObject(testClass, jsonSerializerSettings); + + // Assert + output.Should().Be(expected); + } + + [Fact] + public void PopulatedList_Serialised() + { + // Arrange + var testClass = new TestClass { OptionalList = new List { "foo" } }; + const string expected = "{\"optionalList\":[\"foo\"]}"; + + // Act + var output = JsonConvert.SerializeObject(testClass, jsonSerializerSettings); + + // Assert + output.Should().Be(expected); + } + + [Fact] + public void NullList_NotSerialised_IfMarkedAsRequired() + { + // Arrange + var testClass = new TestClass { RequiredList = null }; + const string expected = "{}"; + + // Act + var output = JsonConvert.SerializeObject(testClass, jsonSerializerSettings); + + // Assert + output.Should().Be(expected); + } + + [Fact] + public void EmptyList_Serialised_IfMarkedAsRequired() + { + // Arrange + var testClass = new TestClass { RequiredList = new List() }; + const string expected = "{\"requiredList\":[]}"; + + // Act + var output = JsonConvert.SerializeObject(testClass, jsonSerializerSettings); + + // Assert + output.Should().Be(expected); + } + + [Fact] + public void PopulatedList_Serialised_IfMarkedAsRequired() + { + // Arrange + var testClass = new TestClass { RequiredList = new List { "foo" } }; + const string expected = "{\"requiredList\":[\"foo\"]}"; + + // Act + var output = JsonConvert.SerializeObject(testClass, jsonSerializerSettings); + + // Assert + output.Should().Be(expected); + } + + [Fact] + public void List_SerialisedAsObject_IfSingleElement_AndMarkedAsObjectIfSingle() + { + // Arrange + var testClass = new TestClass { OneOrMore = new List { "foo" } }; + const string expected = "{\"oneOrMore\":\"foo\"}"; + + // Act + var output = JsonConvert.SerializeObject(testClass, jsonSerializerSettings); + + // Assert + output.Should().Be(expected); + } + + [Fact] + public void List_SerialisedAsArray_IfMultipleElements_AndMarkedAsObjectIfSingle() + { + // Arrange + var testClass = new TestClass { OneOrMore = new List { "foo", "bar" } }; + const string expected = "{\"oneOrMore\":[\"foo\",\"bar\"]}"; + + // Act + var output = JsonConvert.SerializeObject(testClass, jsonSerializerSettings); + + // Assert + output.Should().Be(expected); + } + + private class TestClass + { + public List OptionalList { get; set; } + + [RequiredOutput] public List RequiredList { get; set; } + + [ObjectIfSingle] public List OneOrMore { get; set; } + + public int Width { get; set; } + + public int Height { get; set; } + } + + private class NullableTestClass + { + public int? Width { get; set; } + + public int? Height { get; set; } } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Serialisation/TargetConverterTests.cs b/src/IIIF/IIIF.Tests/Serialisation/TargetConverterTests.cs index 72daf59..f1c86bf 100644 --- a/src/IIIF/IIIF.Tests/Serialisation/TargetConverterTests.cs +++ b/src/IIIF/IIIF.Tests/Serialisation/TargetConverterTests.cs @@ -6,70 +6,70 @@ using Newtonsoft.Json; using Xunit; -namespace IIIF.Tests.Serialisation +namespace IIIF.Tests.Serialisation; + +public class TargetConverterTests { - public class TargetConverterTests + private readonly TargetConverter sut; + + public TargetConverterTests() + { + sut = new TargetConverter(); + } + + [Fact] + public void ConvertCanvas_NoDimensions_OutputsIdOnly() { - private readonly TargetConverter sut; - public TargetConverterTests() - { - sut = new TargetConverter(); - } + // Arrange + const string testId = "https://test.example.com/canvas"; + var canvas = new Canvas { Id = testId }; - [Fact] - public void ConvertCanvas_NoDimensions_OutputsIdOnly() - { - // Arrange - const string testId = "https://test.example.com/canvas"; - var canvas = new Canvas { Id = testId }; - - // Act - var result = JsonConvert.SerializeObject(canvas, Formatting.None, sut); - - // Assert - result.Should().Be($"\"{testId}\""); - } - - [Fact] - public void ConvertCanvas_EmptyItems_OutputsIdOnly() - { - // Arrange - const string testId = "https://test.example.com/canvas"; - var canvas = new Canvas { Id = testId, Items = new List(0) }; + // Act + var result = JsonConvert.SerializeObject(canvas, Formatting.None, sut); + + // Assert + result.Should().Be($"\"{testId}\""); + } + + [Fact] + public void ConvertCanvas_EmptyItems_OutputsIdOnly() + { + // Arrange + const string testId = "https://test.example.com/canvas"; + var canvas = new Canvas { Id = testId, Items = new List(0) }; - // Act - var result = JsonConvert.SerializeObject(canvas, Formatting.None, sut); - - // Assert - result.Should().Be($"\"{testId}\""); - } - - [Fact] - public void ConvertCanvas_SerialiseTargetAsIdTrue_OnlyReturnsId() - { - // Arrange - const string testId = "https://test.example.com/canvas"; - var canvas = new Canvas { Id = testId, Width = 300, Height = 200, SerialiseTargetAsId = true}; + // Act + var result = JsonConvert.SerializeObject(canvas, Formatting.None, sut); - // Act - var result = JsonConvert.SerializeObject(canvas, Formatting.None, sut); - - // Assert - result.Should().Be($"\"{testId}\""); - } + // Assert + result.Should().Be($"\"{testId}\""); + } + + [Fact] + public void ConvertCanvas_SerialiseTargetAsIdTrue_OnlyReturnsId() + { + // Arrange + const string testId = "https://test.example.com/canvas"; + var canvas = new Canvas { Id = testId, Width = 300, Height = 200, SerialiseTargetAsId = true }; + + // Act + var result = JsonConvert.SerializeObject(canvas, Formatting.None, sut); + + // Assert + result.Should().Be($"\"{testId}\""); + } + + [Fact] + public void CanDeserialise_SerialisedIdOnlyCanvas() + { + // Arrange + var canvas = new Canvas { Id = "https://test.example.com/canvas" }; + var serialised = JsonConvert.SerializeObject(canvas, Formatting.None, sut); - [Fact] - public void CanDeserialise_SerialisedIdOnlyCanvas() - { - // Arrange - var canvas = new Canvas { Id = "https://test.example.com/canvas" }; - var serialised = JsonConvert.SerializeObject(canvas, Formatting.None, sut); - - // Act - var deserialised = JsonConvert.DeserializeObject(serialised, sut); + // Act + var deserialised = JsonConvert.DeserializeObject(serialised, sut); - // Assert - deserialised.Should().BeEquivalentTo(canvas); - } + // Assert + deserialised.Should().BeEquivalentTo(canvas); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/Serialisation/XsdDateTimeConverterTests.cs b/src/IIIF/IIIF.Tests/Serialisation/XsdDateTimeConverterTests.cs index 79b3a2b..24cabb3 100644 --- a/src/IIIF/IIIF.Tests/Serialisation/XsdDateTimeConverterTests.cs +++ b/src/IIIF/IIIF.Tests/Serialisation/XsdDateTimeConverterTests.cs @@ -4,52 +4,51 @@ using Newtonsoft.Json; using Xunit; -namespace IIIF.Tests.Serialisation +namespace IIIF.Tests.Serialisation; + +public class XsdDateTimeConverterTests { - public class XsdDateTimeConverterTests + private readonly XsdDateTimeConverter sut = new(); + + [Fact] + public void Convert_UtcDate_Success() + { + // Arrange + var date = new DateTime(2023, 3, 3, 11, 08, 37); + var utcDate = DateTime.SpecifyKind(date, DateTimeKind.Utc); + + // Act + var result = JsonConvert.SerializeObject(utcDate, Formatting.None, sut); + + // Assert + result.Should().Be("\"2023-03-03T11:08:37Z\""); + } + + [Fact] + public void Convert_LocalDate_Success() { - private readonly XsdDateTimeConverter sut = new(); - - [Fact] - public void Convert_UtcDate_Success() - { - // Arrange - var date = new DateTime(2023, 3, 3, 11, 08, 37); - var utcDate = DateTime.SpecifyKind(date, DateTimeKind.Utc); - - // Act - var result = JsonConvert.SerializeObject(utcDate, Formatting.None, sut); - - // Assert - result.Should().Be("\"2023-03-03T11:08:37Z\""); - } - - [Fact] - public void Convert_LocalDate_Success() - { - // Arrange - var date = new DateTime(2023, 3, 3, 11, 08, 37); - var localDate = DateTime.SpecifyKind(date, DateTimeKind.Local); - - // Act - var result = JsonConvert.SerializeObject(localDate, Formatting.None, sut); - - // Assert - result.Should().Be("\"2023-03-03T11:08:37+00:00\""); - } - - [Fact] - public void Convert_UnspecifiedDate_Success() - { - // Arrange - var date = new DateTime(2023, 3, 3, 11, 08, 37); - var unspecifiedDate = DateTime.SpecifyKind(date, DateTimeKind.Unspecified); - - // Act - var result = JsonConvert.SerializeObject(unspecifiedDate, Formatting.None, sut); - - // Assert - result.Should().Be("\"2023-03-03T11:08:37\""); - } + // Arrange + var date = new DateTime(2023, 3, 3, 11, 08, 37); + var localDate = DateTime.SpecifyKind(date, DateTimeKind.Local); + + // Act + var result = JsonConvert.SerializeObject(localDate, Formatting.None, sut); + + // Assert + result.Should().Be("\"2023-03-03T11:08:37+00:00\""); + } + + [Fact] + public void Convert_UnspecifiedDate_Success() + { + // Arrange + var date = new DateTime(2023, 3, 3, 11, 08, 37); + var unspecifiedDate = DateTime.SpecifyKind(date, DateTimeKind.Unspecified); + + // Act + var result = JsonConvert.SerializeObject(unspecifiedDate, Formatting.None, sut); + + // Assert + result.Should().Be("\"2023-03-03T11:08:37\""); } } \ No newline at end of file diff --git a/src/IIIF/IIIF.Tests/StringAssertionX.cs b/src/IIIF/IIIF.Tests/StringAssertionX.cs index 91423bc..a385c27 100644 --- a/src/IIIF/IIIF.Tests/StringAssertionX.cs +++ b/src/IIIF/IIIF.Tests/StringAssertionX.cs @@ -1,16 +1,15 @@ using FluentAssertions; -namespace IIIF.Tests +namespace IIIF.Tests; + +public static class StringAssertionX { - public static class StringAssertionX + /// + /// Uses .Should().Be() fluent assertion but handles possible line ending differences + /// + public static void ShouldMatchJson(this string json, string expected, string because = "", + params object[] becauseArgs) { - /// - /// Uses .Should().Be() fluent assertion but handles possible line ending differences - /// - public static void ShouldMatchJson(this string json, string expected, string because = "", - params object[] becauseArgs) - { - json.Replace("\r\n", "\n").Should().Be(expected.Replace("\r\n", "\n"), because, becauseArgs); - } + json.Replace("\r\n", "\n").Should().Be(expected.Replace("\r\n", "\n"), because, becauseArgs); } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Auth/V0/AccessTokenService/AccessTokenError.cs b/src/IIIF/IIIF/Auth/V0/AccessTokenService/AccessTokenError.cs index e06022f..f60175f 100644 --- a/src/IIIF/IIIF/Auth/V0/AccessTokenService/AccessTokenError.cs +++ b/src/IIIF/IIIF/Auth/V0/AccessTokenService/AccessTokenError.cs @@ -1,77 +1,76 @@ using System.ComponentModel; using IIIF.Serialisation; -using Newtonsoft.Json; -namespace IIIF.Auth.V0.AccessTokenService -{ - /// - /// Response from access token service in the result of an error - /// - /// - /// See https://iiif.io/api/auth/0.9/#access-token-error-conditions - /// - public class AccessTokenError : JsonLdBase - { - [JsonProperty(PropertyName = "error", Order = 1)] - [CamelCaseEnum] - public AccessTokenErrorConditions Error { get; set; } - - [JsonProperty(PropertyName = "description", Order = 2)] - public string Description { get; set; } +namespace IIIF.Auth.V0.AccessTokenService; - public AccessTokenError() - { - } +/// +/// Response from access token service in the result of an error +/// +/// +/// See https://iiif.io/api/auth/0.9/#access-token-error-conditions +/// +public class AccessTokenError : JsonLdBase +{ + [JsonProperty(PropertyName = "error", Order = 1)] + [CamelCaseEnum] + public AccessTokenErrorConditions Error { get; set; } - /// - /// Create new AccessTokenError using provided error condition and default description for condition - /// - public AccessTokenError(AccessTokenErrorConditions error) : this(error, error.GetDescription()) - { - } + [JsonProperty(PropertyName = "description", Order = 2)] + public string Description { get; set; } - /// - /// Create new AccessTokenError using provided error condition and custom description of error - /// - public AccessTokenError(AccessTokenErrorConditions error, string description) - { - Error = error; - Description = description; - } + public AccessTokenError() + { } + /// + /// Create new AccessTokenError using provided error condition and default description for condition + /// + public AccessTokenError(AccessTokenErrorConditions error) : this(error, error.GetDescription()) + { + } - public enum AccessTokenErrorConditions + /// + /// Create new AccessTokenError using provided error condition and custom description of error + /// + public AccessTokenError(AccessTokenErrorConditions error, string description) { - /// - /// The service could not process the information sent in the body of the request. - /// - [Description("The service could not process the information sent in the body of the request.")] - InvalidRequest, + Error = error; + Description = description; + } +} - /// - /// The request did not have the credentials required. - /// - [Description("The request did not have the credentials required.")] - MissingCredentials, +public enum AccessTokenErrorConditions +{ + /// + /// The service could not process the information sent in the body of the request. + /// + [Description("The service could not process the information sent in the body of the request.")] + InvalidRequest, - /// - /// The request had credentials that are not valid for the service. - /// - [Description("The request had credentials that are not valid for the service.")] - InvalidCredentials, + /// + /// The request did not have the credentials required. + /// + [Description("The request did not have the credentials required.")] + MissingCredentials, - /// - /// The request came from a different origin than that specified in the access cookie service request, - /// or an origin that the server rejects for other reasons. - /// - [Description("The request came from a different origin than that specified in the access cookie service request, or an origin that the server rejects for other reasons.")] - InvalidOrigin, + /// + /// The request had credentials that are not valid for the service. + /// + [Description("The request had credentials that are not valid for the service.")] + InvalidCredentials, - /// - /// The request could not be fulfilled for reasons other than those listed above, such as scheduled maintenance. - /// - [Description("The request could not be fulfilled for reasons other than those listed above, such as scheduled maintenance.")] - Unavailable - } + /// + /// The request came from a different origin than that specified in the access cookie service request, + /// or an origin that the server rejects for other reasons. + /// + [Description( + "The request came from a different origin than that specified in the access cookie service request, or an origin that the server rejects for other reasons.")] + InvalidOrigin, + + /// + /// The request could not be fulfilled for reasons other than those listed above, such as scheduled maintenance. + /// + [Description( + "The request could not be fulfilled for reasons other than those listed above, such as scheduled maintenance.")] + Unavailable } \ No newline at end of file diff --git a/src/IIIF/IIIF/Auth/V0/AccessTokenService/AccessTokenResponse.cs b/src/IIIF/IIIF/Auth/V0/AccessTokenService/AccessTokenResponse.cs index 144455e..44e1d07 100644 --- a/src/IIIF/IIIF/Auth/V0/AccessTokenService/AccessTokenResponse.cs +++ b/src/IIIF/IIIF/Auth/V0/AccessTokenService/AccessTokenResponse.cs @@ -1,22 +1,19 @@ -using Newtonsoft.Json; +namespace IIIF.Auth.V0.AccessTokenService; -namespace IIIF.Auth.V0.AccessTokenService +/// +/// Access Token response sent by access token service +/// +/// +/// See https://iiif.io/api/auth/0.9/#the-json-access-token-response +/// +public class AccessTokenResponse : JsonLdBase { - /// - /// Access Token response sent by access token service - /// - /// - /// See https://iiif.io/api/auth/0.9/#the-json-access-token-response - /// - public class AccessTokenResponse : JsonLdBase - { - [JsonProperty(PropertyName = "messageId", Order = 1)] - public string MessageId { get; set; } - - [JsonProperty(PropertyName = "accessToken", Order = 2)] - public string AccessToken { get; set; } - - [JsonProperty(PropertyName = "expiresIn", Order = 3)] - public int ExpiresIn { get; set; } - } + [JsonProperty(PropertyName = "messageId", Order = 1)] + public string MessageId { get; set; } + + [JsonProperty(PropertyName = "accessToken", Order = 2)] + public string AccessToken { get; set; } + + [JsonProperty(PropertyName = "expiresIn", Order = 3)] + public int ExpiresIn { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Auth/V0/AuthCookieService.cs b/src/IIIF/IIIF/Auth/V0/AuthCookieService.cs index dce1cee..6108786 100644 --- a/src/IIIF/IIIF/Auth/V0/AuthCookieService.cs +++ b/src/IIIF/IIIF/Auth/V0/AuthCookieService.cs @@ -2,54 +2,64 @@ using IIIF.Presentation.V2; using IIIF.Presentation.V2.Strings; using IIIF.Serialisation; -using Newtonsoft.Json; -namespace IIIF.Auth.V0 +namespace IIIF.Auth.V0; + +/// +/// Used to obtain a cookie for interacting with content +/// +/// https://iiif.io/api/auth/0.9/#access-cookie-service +public class AuthCookieService : ResourceBase, IService { - /// - /// Used to obtain a cookie for interacting with content - /// - /// https://iiif.io/api/auth/0.9/#access-cookie-service - public class AuthCookieService : ResourceBase, IService + public const string LoginProfile = "http://iiif.io/api/auth/0/login"; + public const string ClickthroughProfile = "http://iiif.io/api/auth/0/clickthrough"; + public const string KioskProfile = "http://iiif.io/api/auth/0/kiosk"; + public const string ExternalProfile = "http://iiif.io/api/auth/0/external"; + + public AuthCookieService(string profile) { - public const string LoginProfile = "http://iiif.io/api/auth/0/login"; - public const string ClickthroughProfile = "http://iiif.io/api/auth/0/clickthrough"; - public const string KioskProfile = "http://iiif.io/api/auth/0/kiosk"; - public const string ExternalProfile = "http://iiif.io/api/auth/0/external"; + Profile = profile; + } - public AuthCookieService(string profile) - { - Profile = profile; - } - - [JsonProperty(PropertyName = "@type", Order = 3)] - public override string? Type { get; set; } = "AuthCookieService0"; + [JsonProperty(PropertyName = "@type", Order = 3)] + public override string? Type { get; set; } = "AuthCookieService0"; - [JsonProperty(Order = 12, PropertyName = "description")] - public MetaDataValue Description { get; set; } + [JsonProperty(Order = 12, PropertyName = "description")] + public MetaDataValue Description { get; set; } - [JsonProperty(Order = 26, PropertyName = "service")] - [ObjectIfSingle] - public List Service { get; set; } - - [JsonProperty(Order = 103, PropertyName = "confirmLabel")] - public MetaDataValue? ConfirmLabel { get; set; } + [JsonProperty(Order = 26, PropertyName = "service")] + [ObjectIfSingle] + public List Service { get; set; } - [JsonProperty(Order = 111, PropertyName = "header")] - public MetaDataValue? Header { get; set; } + [JsonProperty(Order = 103, PropertyName = "confirmLabel")] + public MetaDataValue? ConfirmLabel { get; set; } - [JsonProperty(Order = 121, PropertyName = "failureHeader")] - public MetaDataValue? FailureHeader { get; set; } + [JsonProperty(Order = 111, PropertyName = "header")] + public MetaDataValue? Header { get; set; } - [JsonProperty(Order = 122, PropertyName = "failureDescription")] - public MetaDataValue? FailureDescription { get; set; } - - public static AuthCookieService NewLoginInstance() => new(LoginProfile); + [JsonProperty(Order = 121, PropertyName = "failureHeader")] + public MetaDataValue? FailureHeader { get; set; } + + [JsonProperty(Order = 122, PropertyName = "failureDescription")] + public MetaDataValue? FailureDescription { get; set; } + + public static AuthCookieService NewLoginInstance() + { + return new(LoginProfile); + } - public static AuthCookieService NewClickthroughInstance() => new(ClickthroughProfile); + public static AuthCookieService NewClickthroughInstance() + { + return new(ClickthroughProfile); + } - public static AuthCookieService NewKioskInstance() => new(KioskProfile); + public static AuthCookieService NewKioskInstance() + { + return new(KioskProfile); + } - public static AuthCookieService NewExternalInstance() => new(ExternalProfile); + public static AuthCookieService NewExternalInstance() + { + return new(ExternalProfile); } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Auth/V0/AuthLogoutService.cs b/src/IIIF/IIIF/Auth/V0/AuthLogoutService.cs index fa5afd4..aa8c308 100644 --- a/src/IIIF/IIIF/Auth/V0/AuthLogoutService.cs +++ b/src/IIIF/IIIF/Auth/V0/AuthLogoutService.cs @@ -1,22 +1,20 @@ using IIIF.Presentation.V2; -using Newtonsoft.Json; -namespace IIIF.Auth.V0 -{ - /// - /// Used to logout of session - /// - /// https://iiif.io/api/auth/0.9/#logout-service - public class AuthLogoutService : ResourceBase, IService - { - public const string AuthLogout0Profile = "http://iiif.io/api/auth/0/logout"; +namespace IIIF.Auth.V0; - public AuthLogoutService() - { - Profile = AuthLogout0Profile; - } +/// +/// Used to logout of session +/// +/// https://iiif.io/api/auth/0.9/#logout-service +public class AuthLogoutService : ResourceBase, IService +{ + public const string AuthLogout0Profile = "http://iiif.io/api/auth/0/logout"; - [JsonProperty(PropertyName = "@type", Order = 3)] - public override string? Type { get; set; } = "AuthLogoutService0"; + public AuthLogoutService() + { + Profile = AuthLogout0Profile; } + + [JsonProperty(PropertyName = "@type", Order = 3)] + public override string? Type { get; set; } = "AuthLogoutService0"; } \ No newline at end of file diff --git a/src/IIIF/IIIF/Auth/V0/AuthTokenService.cs b/src/IIIF/IIIF/Auth/V0/AuthTokenService.cs index 1dc55cb..516ab4b 100644 --- a/src/IIIF/IIIF/Auth/V0/AuthTokenService.cs +++ b/src/IIIF/IIIF/Auth/V0/AuthTokenService.cs @@ -1,22 +1,20 @@ using IIIF.Presentation.V2; -using Newtonsoft.Json; -namespace IIIF.Auth.V0 +namespace IIIF.Auth.V0; + +/// +/// Used to obtain an access token for accessing a Description Resource +/// +/// https://iiif.io/api/auth/0.9/#access-token-service +public class AuthTokenService : ResourceBase, IService { - /// - /// Used to obtain an access token for accessing a Description Resource - /// - /// https://iiif.io/api/auth/0.9/#access-token-service - public class AuthTokenService : ResourceBase, IService + public const string AuthToken0Profile = "http://iiif.io/api/auth/0/token"; + + public AuthTokenService() { - public const string AuthToken0Profile = "http://iiif.io/api/auth/0/token"; - - public AuthTokenService() - { - Profile = AuthToken0Profile; - } - - [JsonProperty(PropertyName = "@type", Order = 3)] - public override string? Type { get; set; } = "AuthTokenService0"; + Profile = AuthToken0Profile; } + + [JsonProperty(PropertyName = "@type", Order = 3)] + public override string? Type { get; set; } = "AuthTokenService0"; } \ No newline at end of file diff --git a/src/IIIF/IIIF/Auth/V0/Constants.cs b/src/IIIF/IIIF/Auth/V0/Constants.cs index 5799ec0..9f30e29 100644 --- a/src/IIIF/IIIF/Auth/V0/Constants.cs +++ b/src/IIIF/IIIF/Auth/V0/Constants.cs @@ -1,7 +1,6 @@ -namespace IIIF.Auth.V0 +namespace IIIF.Auth.V0; + +public class Constants { - public class Constants - { - public const string IIIFAuthContext = "http://iiif.io/api/auth/0/context.json"; - } + public const string IIIFAuthContext = "http://iiif.io/api/auth/0/context.json"; } \ No newline at end of file diff --git a/src/IIIF/IIIF/Auth/V1/AccessTokenService/AccessTokenError.cs b/src/IIIF/IIIF/Auth/V1/AccessTokenService/AccessTokenError.cs index 9bff406..6f6dc91 100644 --- a/src/IIIF/IIIF/Auth/V1/AccessTokenService/AccessTokenError.cs +++ b/src/IIIF/IIIF/Auth/V1/AccessTokenService/AccessTokenError.cs @@ -1,77 +1,76 @@ using System.ComponentModel; using IIIF.Serialisation; -using Newtonsoft.Json; -namespace IIIF.Auth.V1.AccessTokenService -{ - /// - /// Response from access token service in the result of an error - /// - /// - /// See https://iiif.io/api/auth/1.0/#access-token-error-conditions - /// - public class AccessTokenError : JsonLdBase - { - [JsonProperty(PropertyName = "error", Order = 1)] - [CamelCaseEnum] - public AccessTokenErrorConditions Error { get; set; } - - [JsonProperty(PropertyName = "description", Order = 2)] - public string Description { get; set; } +namespace IIIF.Auth.V1.AccessTokenService; - public AccessTokenError() - { - } +/// +/// Response from access token service in the result of an error +/// +/// +/// See https://iiif.io/api/auth/1.0/#access-token-error-conditions +/// +public class AccessTokenError : JsonLdBase +{ + [JsonProperty(PropertyName = "error", Order = 1)] + [CamelCaseEnum] + public AccessTokenErrorConditions Error { get; set; } - /// - /// Create new AccessTokenError using provided error condition and default description for condition - /// - public AccessTokenError(AccessTokenErrorConditions error) : this(error, error.GetDescription()) - { - } + [JsonProperty(PropertyName = "description", Order = 2)] + public string Description { get; set; } - /// - /// Create new AccessTokenError using provided error condition and custom description of error - /// - public AccessTokenError(AccessTokenErrorConditions error, string description) - { - Error = error; - Description = description; - } + public AccessTokenError() + { } + /// + /// Create new AccessTokenError using provided error condition and default description for condition + /// + public AccessTokenError(AccessTokenErrorConditions error) : this(error, error.GetDescription()) + { + } - public enum AccessTokenErrorConditions + /// + /// Create new AccessTokenError using provided error condition and custom description of error + /// + public AccessTokenError(AccessTokenErrorConditions error, string description) { - /// - /// The service could not process the information sent in the body of the request. - /// - [Description("The service could not process the information sent in the body of the request.")] - InvalidRequest, + Error = error; + Description = description; + } +} - /// - /// The request did not have the credentials required. - /// - [Description("The request did not have the credentials required.")] - MissingCredentials, +public enum AccessTokenErrorConditions +{ + /// + /// The service could not process the information sent in the body of the request. + /// + [Description("The service could not process the information sent in the body of the request.")] + InvalidRequest, - /// - /// The request had credentials that are not valid for the service. - /// - [Description("The request had credentials that are not valid for the service.")] - InvalidCredentials, + /// + /// The request did not have the credentials required. + /// + [Description("The request did not have the credentials required.")] + MissingCredentials, - /// - /// The request came from a different origin than that specified in the access cookie service request, - /// or an origin that the server rejects for other reasons. - /// - [Description("The request came from a different origin than that specified in the access cookie service request, or an origin that the server rejects for other reasons.")] - InvalidOrigin, + /// + /// The request had credentials that are not valid for the service. + /// + [Description("The request had credentials that are not valid for the service.")] + InvalidCredentials, - /// - /// The request could not be fulfilled for reasons other than those listed above, such as scheduled maintenance. - /// - [Description("The request could not be fulfilled for reasons other than those listed above, such as scheduled maintenance.")] - Unavailable - } + /// + /// The request came from a different origin than that specified in the access cookie service request, + /// or an origin that the server rejects for other reasons. + /// + [Description( + "The request came from a different origin than that specified in the access cookie service request, or an origin that the server rejects for other reasons.")] + InvalidOrigin, + + /// + /// The request could not be fulfilled for reasons other than those listed above, such as scheduled maintenance. + /// + [Description( + "The request could not be fulfilled for reasons other than those listed above, such as scheduled maintenance.")] + Unavailable } \ No newline at end of file diff --git a/src/IIIF/IIIF/Auth/V1/AccessTokenService/AccessTokenResponse.cs b/src/IIIF/IIIF/Auth/V1/AccessTokenService/AccessTokenResponse.cs index d9a71d5..195988b 100644 --- a/src/IIIF/IIIF/Auth/V1/AccessTokenService/AccessTokenResponse.cs +++ b/src/IIIF/IIIF/Auth/V1/AccessTokenService/AccessTokenResponse.cs @@ -1,22 +1,19 @@ -using Newtonsoft.Json; +namespace IIIF.Auth.V1.AccessTokenService; -namespace IIIF.Auth.V1.AccessTokenService +/// +/// Access Token response sent by access token service +/// +/// +/// See https://iiif.io/api/auth/1.0/#the-json-access-token-response +/// +public class AccessTokenResponse : JsonLdBase { - /// - /// Access Token response sent by access token service - /// - /// - /// See https://iiif.io/api/auth/1.0/#the-json-access-token-response - /// - public class AccessTokenResponse : JsonLdBase - { - [JsonProperty(PropertyName = "messageId", Order = 1)] - public string MessageId { get; set; } - - [JsonProperty(PropertyName = "accessToken", Order = 2)] - public string AccessToken { get; set; } - - [JsonProperty(PropertyName = "expiresIn", Order = 3)] - public int ExpiresIn { get; set; } - } + [JsonProperty(PropertyName = "messageId", Order = 1)] + public string MessageId { get; set; } + + [JsonProperty(PropertyName = "accessToken", Order = 2)] + public string AccessToken { get; set; } + + [JsonProperty(PropertyName = "expiresIn", Order = 3)] + public int ExpiresIn { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Auth/V1/AuthCookieService.cs b/src/IIIF/IIIF/Auth/V1/AuthCookieService.cs index 7840d0e..c24c351 100644 --- a/src/IIIF/IIIF/Auth/V1/AuthCookieService.cs +++ b/src/IIIF/IIIF/Auth/V1/AuthCookieService.cs @@ -2,54 +2,64 @@ using IIIF.Presentation.V2; using IIIF.Presentation.V2.Strings; using IIIF.Serialisation; -using Newtonsoft.Json; -namespace IIIF.Auth.V1 +namespace IIIF.Auth.V1; + +/// +/// Used to obtain a cookie for interacting with content +/// +/// https://iiif.io/api/auth/1.0/#access-cookie-service +public class AuthCookieService : ResourceBase, IService { - /// - /// Used to obtain a cookie for interacting with content - /// - /// https://iiif.io/api/auth/1.0/#access-cookie-service - public class AuthCookieService : ResourceBase, IService + public const string LoginProfile = "http://iiif.io/api/auth/1/login"; + public const string ClickthroughProfile = "http://iiif.io/api/auth/1/clickthrough"; + public const string KioskProfile = "http://iiif.io/api/auth/1/kiosk"; + public const string ExternalProfile = "http://iiif.io/api/auth/1/external"; + + public AuthCookieService(string profile) { - public const string LoginProfile = "http://iiif.io/api/auth/1/login"; - public const string ClickthroughProfile = "http://iiif.io/api/auth/1/clickthrough"; - public const string KioskProfile = "http://iiif.io/api/auth/1/kiosk"; - public const string ExternalProfile = "http://iiif.io/api/auth/1/external"; + Profile = profile; + } - public AuthCookieService(string profile) - { - Profile = profile; - } - - [JsonProperty(PropertyName = "@type", Order = 3)] - public override string? Type { get; set; } = "AuthCookieService1"; + [JsonProperty(PropertyName = "@type", Order = 3)] + public override string? Type { get; set; } = "AuthCookieService1"; - [JsonProperty(Order = 12, PropertyName = "description")] - public MetaDataValue Description { get; set; } + [JsonProperty(Order = 12, PropertyName = "description")] + public MetaDataValue Description { get; set; } - [JsonProperty(Order = 26, PropertyName = "service")] - [ObjectIfSingle] - public List Service { get; set; } - - [JsonProperty(Order = 103, PropertyName = "confirmLabel")] - public MetaDataValue? ConfirmLabel { get; set; } + [JsonProperty(Order = 26, PropertyName = "service")] + [ObjectIfSingle] + public List Service { get; set; } - [JsonProperty(Order = 111, PropertyName = "header")] - public MetaDataValue? Header { get; set; } + [JsonProperty(Order = 103, PropertyName = "confirmLabel")] + public MetaDataValue? ConfirmLabel { get; set; } - [JsonProperty(Order = 121, PropertyName = "failureHeader")] - public MetaDataValue? FailureHeader { get; set; } + [JsonProperty(Order = 111, PropertyName = "header")] + public MetaDataValue? Header { get; set; } - [JsonProperty(Order = 122, PropertyName = "failureDescription")] - public MetaDataValue? FailureDescription { get; set; } - - public static AuthCookieService NewLoginInstance() => new(LoginProfile); + [JsonProperty(Order = 121, PropertyName = "failureHeader")] + public MetaDataValue? FailureHeader { get; set; } + + [JsonProperty(Order = 122, PropertyName = "failureDescription")] + public MetaDataValue? FailureDescription { get; set; } + + public static AuthCookieService NewLoginInstance() + { + return new(LoginProfile); + } - public static AuthCookieService NewClickthroughInstance() => new(ClickthroughProfile); + public static AuthCookieService NewClickthroughInstance() + { + return new(ClickthroughProfile); + } - public static AuthCookieService NewKioskInstance() => new(KioskProfile); + public static AuthCookieService NewKioskInstance() + { + return new(KioskProfile); + } - public static AuthCookieService NewExternalInstance() => new(ExternalProfile); + public static AuthCookieService NewExternalInstance() + { + return new(ExternalProfile); } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Auth/V1/AuthLogoutService.cs b/src/IIIF/IIIF/Auth/V1/AuthLogoutService.cs index 4bbeb2a..861594e 100644 --- a/src/IIIF/IIIF/Auth/V1/AuthLogoutService.cs +++ b/src/IIIF/IIIF/Auth/V1/AuthLogoutService.cs @@ -1,22 +1,20 @@ using IIIF.Presentation.V2; -using Newtonsoft.Json; -namespace IIIF.Auth.V1 -{ - /// - /// Used to logout of session - /// - /// https://iiif.io/api/auth/1.0/#logout-service - public class AuthLogoutService : ResourceBase, IService - { - public const string AuthLogout1Profile = "http://iiif.io/api/auth/1/logout"; +namespace IIIF.Auth.V1; - public AuthLogoutService() - { - Profile = AuthLogout1Profile; - } +/// +/// Used to logout of session +/// +/// https://iiif.io/api/auth/1.0/#logout-service +public class AuthLogoutService : ResourceBase, IService +{ + public const string AuthLogout1Profile = "http://iiif.io/api/auth/1/logout"; - [JsonProperty(PropertyName = "@type", Order = 3)] - public override string? Type { get; set; } = "AuthLogoutService1"; + public AuthLogoutService() + { + Profile = AuthLogout1Profile; } + + [JsonProperty(PropertyName = "@type", Order = 3)] + public override string? Type { get; set; } = "AuthLogoutService1"; } \ No newline at end of file diff --git a/src/IIIF/IIIF/Auth/V1/AuthTokenService.cs b/src/IIIF/IIIF/Auth/V1/AuthTokenService.cs index 6747d93..b5c3682 100644 --- a/src/IIIF/IIIF/Auth/V1/AuthTokenService.cs +++ b/src/IIIF/IIIF/Auth/V1/AuthTokenService.cs @@ -1,22 +1,20 @@ using IIIF.Presentation.V2; -using Newtonsoft.Json; -namespace IIIF.Auth.V1 +namespace IIIF.Auth.V1; + +/// +/// Used to obtain an access token for accessing a Description Resource +/// +/// https://iiif.io/api/auth/1.0/#access-token-service +public class AuthTokenService : ResourceBase, IService { - /// - /// Used to obtain an access token for accessing a Description Resource - /// - /// https://iiif.io/api/auth/1.0/#access-token-service - public class AuthTokenService : ResourceBase, IService + public const string AuthToken1Profile = "http://iiif.io/api/auth/1/token"; + + public AuthTokenService() { - public const string AuthToken1Profile = "http://iiif.io/api/auth/1/token"; - - public AuthTokenService() - { - Profile = AuthToken1Profile; - } - - [JsonProperty(PropertyName = "@type", Order = 3)] - public override string? Type { get; set; } = "AuthTokenService1"; + Profile = AuthToken1Profile; } + + [JsonProperty(PropertyName = "@type", Order = 3)] + public override string? Type { get; set; } = "AuthTokenService1"; } \ No newline at end of file diff --git a/src/IIIF/IIIF/Auth/V1/Constants.cs b/src/IIIF/IIIF/Auth/V1/Constants.cs index 02cef6e..020d6b6 100644 --- a/src/IIIF/IIIF/Auth/V1/Constants.cs +++ b/src/IIIF/IIIF/Auth/V1/Constants.cs @@ -1,7 +1,6 @@ -namespace IIIF.Auth.V1 +namespace IIIF.Auth.V1; + +public class Constants { - public class Constants - { - public const string IIIFAuthContext = "http://iiif.io/api/auth/1/context.json"; - } + public const string IIIFAuthContext = "http://iiif.io/api/auth/1/context.json"; } \ No newline at end of file diff --git a/src/IIIF/IIIF/Auth/V2/AuthAccessService2.cs b/src/IIIF/IIIF/Auth/V2/AuthAccessService2.cs index 7048bdd..23cfec1 100644 --- a/src/IIIF/IIIF/Auth/V2/AuthAccessService2.cs +++ b/src/IIIF/IIIF/Auth/V2/AuthAccessService2.cs @@ -1,24 +1,22 @@ using IIIF.Presentation.V3; using IIIF.Presentation.V3.Strings; -using Newtonsoft.Json; -namespace IIIF.Auth.V2 +namespace IIIF.Auth.V2; + +public class AuthAccessService2 : ResourceBase, IService { - public class AuthAccessService2 : ResourceBase, IService - { - public const string InteractiveProfile = "interactive"; - public const string KioskProfile = "kiosk"; - public const string ExternalProfile = "external"; - - public override string Type => nameof(AuthAccessService2); + public const string InteractiveProfile = "interactive"; + public const string KioskProfile = "kiosk"; + public const string ExternalProfile = "external"; + + public override string Type => nameof(AuthAccessService2); + + [JsonProperty(Order = 101, PropertyName = "confirmLabel")] + public LanguageMap? ConfirmLabel { get; set; } - [JsonProperty(Order = 101, PropertyName = "confirmLabel")] - public LanguageMap? ConfirmLabel { get; set; } + [JsonProperty(Order = 102, PropertyName = "heading")] + public LanguageMap? Heading { get; set; } - [JsonProperty(Order = 102, PropertyName = "heading")] - public LanguageMap? Heading { get; set; } - - [JsonProperty(Order = 103, PropertyName = "note")] - public LanguageMap? Note { get; set; } - } + [JsonProperty(Order = 103, PropertyName = "note")] + public LanguageMap? Note { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Auth/V2/AuthAccessToken2.cs b/src/IIIF/IIIF/Auth/V2/AuthAccessToken2.cs index aa7d99f..af603ad 100644 --- a/src/IIIF/IIIF/Auth/V2/AuthAccessToken2.cs +++ b/src/IIIF/IIIF/Auth/V2/AuthAccessToken2.cs @@ -1,23 +1,19 @@ -using Newtonsoft.Json; +namespace IIIF.Auth.V2; -namespace IIIF.Auth.V2 +public class AuthAccessToken2 : JsonLdBase { - public class AuthAccessToken2 : JsonLdBase - { - [JsonProperty(Order = 1, PropertyName = "@context")] - public new string Context = Constants.IIIFAuth2Context; - - [JsonProperty(Order = 1, PropertyName = "type")] - public string Type => nameof(AuthAccessToken2); - - [JsonProperty(Order = 10, PropertyName = "messageId")] - public string? MessageId { get; set; } - - [JsonProperty(Order = 20, PropertyName = "accessToken")] - public string? AccessToken { get; set; } - - [JsonProperty(Order = 30, PropertyName = "expiresIn")] - public int? ExpiresIn { get; set; } - - } + [JsonProperty(Order = 1, PropertyName = "@context")] + public new string Context = Constants.IIIFAuth2Context; + + [JsonProperty(Order = 1, PropertyName = "type")] + public string Type => nameof(AuthAccessToken2); + + [JsonProperty(Order = 10, PropertyName = "messageId")] + public string? MessageId { get; set; } + + [JsonProperty(Order = 20, PropertyName = "accessToken")] + public string? AccessToken { get; set; } + + [JsonProperty(Order = 30, PropertyName = "expiresIn")] + public int? ExpiresIn { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Auth/V2/AuthAccessTokenError2.cs b/src/IIIF/IIIF/Auth/V2/AuthAccessTokenError2.cs index b8c1a3b..e40c1e9 100644 --- a/src/IIIF/IIIF/Auth/V2/AuthAccessTokenError2.cs +++ b/src/IIIF/IIIF/Auth/V2/AuthAccessTokenError2.cs @@ -1,34 +1,33 @@ using IIIF.Presentation.V3; using IIIF.Presentation.V3.Strings; -using Newtonsoft.Json; -namespace IIIF.Auth.V2 +namespace IIIF.Auth.V2; + +public class AuthAccessTokenError2 : ResourceBase { - public class AuthAccessTokenError2 : ResourceBase + public const string InvalidRequest = "invalidRequest"; + public const string InvalidOrigin = "invalidOrigin"; + public const string MissingAspect = "missingAspect"; + public const string InvalidAspect = "invalidAspect"; + public const string ExpiredAspect = "expiredAspect"; + public const string Unavailable = "unavailable"; + + public override string Type => nameof(AuthAccessTokenError2); + + [JsonProperty(Order = 101, PropertyName = "heading")] + public LanguageMap? Heading { get; set; } + + [JsonProperty(Order = 102, PropertyName = "note")] + public LanguageMap? Note { get; set; } + + public AuthAccessTokenError2(string profile, LanguageMap note) { - public const string InvalidRequest = "invalidRequest"; - public const string InvalidOrigin = "invalidOrigin"; - public const string MissingAspect = "missingAspect"; - public const string InvalidAspect = "invalidAspect"; - public const string ExpiredAspect = "expiredAspect"; - public const string Unavailable = "unavailable"; - - public override string Type => nameof(AuthAccessTokenError2); - - [JsonProperty(Order = 101, PropertyName = "heading")] - public LanguageMap? Heading { get; set; } - - [JsonProperty(Order = 102, PropertyName = "note")] - public LanguageMap? Note { get; set; } + Context = Constants.IIIFAuth2Context; + Profile = profile; + Note = note; + } - public AuthAccessTokenError2(string profile, LanguageMap note) - { - Context = Constants.IIIFAuth2Context; - Profile = profile; - Note = note; - } - - public AuthAccessTokenError2() - { } + public AuthAccessTokenError2() + { } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Auth/V2/AuthAccessTokenService2.cs b/src/IIIF/IIIF/Auth/V2/AuthAccessTokenService2.cs index 962258e..3beb29d 100644 --- a/src/IIIF/IIIF/Auth/V2/AuthAccessTokenService2.cs +++ b/src/IIIF/IIIF/Auth/V2/AuthAccessTokenService2.cs @@ -1,17 +1,15 @@ using IIIF.Presentation.V3; using IIIF.Presentation.V3.Strings; -using Newtonsoft.Json; -namespace IIIF.Auth.V2 +namespace IIIF.Auth.V2; + +public class AuthAccessTokenService2 : ResourceBase, IService { - public class AuthAccessTokenService2 : ResourceBase, IService - { - public override string Type => nameof(AuthAccessTokenService2); - - [JsonProperty(Order = 102, PropertyName = "errorHeading")] - public LanguageMap? ErrorHeading { get; set; } - - [JsonProperty(Order = 103, PropertyName = "errorNote")] - public LanguageMap? ErrorNote { get; set; } - } + public override string Type => nameof(AuthAccessTokenService2); + + [JsonProperty(Order = 102, PropertyName = "errorHeading")] + public LanguageMap? ErrorHeading { get; set; } + + [JsonProperty(Order = 103, PropertyName = "errorNote")] + public LanguageMap? ErrorNote { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Auth/V2/AuthLogoutService2.cs b/src/IIIF/IIIF/Auth/V2/AuthLogoutService2.cs index a16864d..d2cecd9 100644 --- a/src/IIIF/IIIF/Auth/V2/AuthLogoutService2.cs +++ b/src/IIIF/IIIF/Auth/V2/AuthLogoutService2.cs @@ -1,9 +1,8 @@ using IIIF.Presentation.V3; -namespace IIIF.Auth.V2 +namespace IIIF.Auth.V2; + +public class AuthLogoutService2 : ResourceBase, IService { - public class AuthLogoutService2 : ResourceBase, IService - { - public override string Type => nameof(AuthLogoutService2); - } + public override string Type => nameof(AuthLogoutService2); } \ No newline at end of file diff --git a/src/IIIF/IIIF/Auth/V2/AuthProbeResult2.cs b/src/IIIF/IIIF/Auth/V2/AuthProbeResult2.cs index 7d49887..d14e0a8 100644 --- a/src/IIIF/IIIF/Auth/V2/AuthProbeResult2.cs +++ b/src/IIIF/IIIF/Auth/V2/AuthProbeResult2.cs @@ -1,30 +1,27 @@ -using IIIF.Presentation.V3; using IIIF.Presentation.V3.Strings; -using Newtonsoft.Json; -namespace IIIF.Auth.V2 +namespace IIIF.Auth.V2; + +public class AuthProbeResult2 : JsonLdBase { - public class AuthProbeResult2 : JsonLdBase - { - [JsonProperty(Order = 1, PropertyName = "@context")] - public new string Context = Constants.IIIFAuth2Context; - - [JsonProperty(Order = 1, PropertyName = "type")] - public string Type => nameof(AuthProbeResult2); - - [JsonProperty(Order = 10, PropertyName = "status")] - public int? Status { get; set; } - - [JsonProperty(Order = 20, PropertyName = "substitute")] - public JsonLdBase? Substitute { get; set; } - - [JsonProperty(Order = 40, PropertyName = "location")] - public JsonLdBase? Location { get; set; } - - [JsonProperty(Order = 50, PropertyName = "heading")] - public LanguageMap? Heading { get; set; } - - [JsonProperty(Order = 60, PropertyName = "note")] - public LanguageMap? Note { get; set; } - } + [JsonProperty(Order = 1, PropertyName = "@context")] + public new string Context = Constants.IIIFAuth2Context; + + [JsonProperty(Order = 1, PropertyName = "type")] + public string Type => nameof(AuthProbeResult2); + + [JsonProperty(Order = 10, PropertyName = "status")] + public int? Status { get; set; } + + [JsonProperty(Order = 20, PropertyName = "substitute")] + public JsonLdBase? Substitute { get; set; } + + [JsonProperty(Order = 40, PropertyName = "location")] + public JsonLdBase? Location { get; set; } + + [JsonProperty(Order = 50, PropertyName = "heading")] + public LanguageMap? Heading { get; set; } + + [JsonProperty(Order = 60, PropertyName = "note")] + public LanguageMap? Note { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Auth/V2/AuthProbeService2.cs b/src/IIIF/IIIF/Auth/V2/AuthProbeService2.cs index 62afcbb..f7369c0 100644 --- a/src/IIIF/IIIF/Auth/V2/AuthProbeService2.cs +++ b/src/IIIF/IIIF/Auth/V2/AuthProbeService2.cs @@ -1,18 +1,15 @@ using IIIF.Presentation.V3; -using IIIF.Presentation.V3.Content; using IIIF.Presentation.V3.Strings; -using Newtonsoft.Json; -namespace IIIF.Auth.V2 +namespace IIIF.Auth.V2; + +public class AuthProbeService2 : ResourceBase { - public class AuthProbeService2 : ResourceBase - { - public override string Type => nameof(AuthProbeService2); - - [JsonProperty(Order = 102, PropertyName = "errorHeading")] - public LanguageMap? ErrorHeading { get; set; } - - [JsonProperty(Order = 103, PropertyName = "errorNote")] - public LanguageMap? ErrorNote { get; set; } - } + public override string Type => nameof(AuthProbeService2); + + [JsonProperty(Order = 102, PropertyName = "errorHeading")] + public LanguageMap? ErrorHeading { get; set; } + + [JsonProperty(Order = 103, PropertyName = "errorNote")] + public LanguageMap? ErrorNote { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Auth/V2/Constants.cs b/src/IIIF/IIIF/Auth/V2/Constants.cs index 05197b5..e084d9e 100644 --- a/src/IIIF/IIIF/Auth/V2/Constants.cs +++ b/src/IIIF/IIIF/Auth/V2/Constants.cs @@ -1,7 +1,6 @@ -namespace IIIF.Auth.V2 +namespace IIIF.Auth.V2; + +public class Constants { - public class Constants - { - public const string IIIFAuth2Context = "http://iiif.io/api/auth/2/context.json"; - } + public const string IIIFAuth2Context = "http://iiif.io/api/auth/2/context.json"; } \ No newline at end of file diff --git a/src/IIIF/IIIF/Discovery/ContentTypes.cs b/src/IIIF/IIIF/Discovery/ContentTypes.cs index 130e15e..518ec23 100644 --- a/src/IIIF/IIIF/Discovery/ContentTypes.cs +++ b/src/IIIF/IIIF/Discovery/ContentTypes.cs @@ -1,13 +1,12 @@ -namespace IIIF.Discovery +namespace IIIF.Discovery; + +/// +/// Contains Content-Type/Accepts headers for IIIF Discovery API. +/// +public static class ContentTypes { /// - /// Contains Content-Type/Accepts headers for IIIF Discovery API. + /// Content-Type for change discovery v1. /// - public static class ContentTypes - { - /// - /// Content-Type for change discovery v1. - /// - public const string V1 = "application/ld+json;profile=\"" + Context.ChangeDiscovery1Context + "\""; - } + public const string V1 = "application/ld+json;profile=\"" + Context.ChangeDiscovery1Context + "\""; } \ No newline at end of file diff --git a/src/IIIF/IIIF/Discovery/Context.cs b/src/IIIF/IIIF/Discovery/Context.cs index 9070a18..a31086f 100644 --- a/src/IIIF/IIIF/Discovery/Context.cs +++ b/src/IIIF/IIIF/Discovery/Context.cs @@ -1,7 +1,6 @@ -namespace IIIF.Discovery +namespace IIIF.Discovery; + +public class Context { - public class Context - { - public const string ChangeDiscovery1Context = "http://iiif.io/api/discovery/1/context.json"; - } + public const string ChangeDiscovery1Context = "http://iiif.io/api/discovery/1/context.json"; } \ No newline at end of file diff --git a/src/IIIF/IIIF/Discovery/V1/Activity.cs b/src/IIIF/IIIF/Discovery/V1/Activity.cs index c4f9dbf..3a36238 100644 --- a/src/IIIF/IIIF/Discovery/V1/Activity.cs +++ b/src/IIIF/IIIF/Discovery/V1/Activity.cs @@ -3,138 +3,129 @@ using IIIF.Presentation.V3; using IIIF.Presentation.V3.Content; using IIIF.Serialisation; -using Newtonsoft.Json; -namespace IIIF.Discovery.V1 +namespace IIIF.Discovery.V1; + +/// +/// The Activities are the means of describing the changes that have occurred in the content provider’s system. +/// +/// See https://iiif.io/api/discovery/1.0/#activities +public class Activity : JsonLdBase +{ + [JsonProperty(Order = 2)] public string? Id { get; set; } + + [JsonProperty(Order = 3)] + [EnumAsString] + public ActivityType Type { get; set; } + + /// + /// A short textual description of the Activity + /// + [JsonProperty(Order = 5)] + public string? Summary { get; set; } + + /// + /// The IIIF resource that was affected by the Activity + /// + [JsonProperty(Order = 6)] + public ActivityObject Object { get; set; } + + /// + /// The new location of the IIIF resource, after it was affected by a Move activity. + /// + [JsonProperty(Order = 7)] + public ActivityObject Target { get; set; } + + /// + /// The time at which the Activity was finished. + /// + [JsonProperty(Order = 10)] + [JsonConverter(typeof(XsdDateTimeConverter))] + public DateTime? EndTime { get; set; } + + /// + /// The time at which the Activity was started. + /// + [JsonProperty(Order = 11)] + [JsonConverter(typeof(XsdDateTimeConverter))] + public DateTime? StartTime { get; set; } + + /// + /// The organization, person, or software agent that carried out the Activity. + /// + [JsonProperty(Order = 21)] + public Actor? Actor { get; set; } +} + +public class ActivityObject : IService +{ + [JsonProperty(Order = 2)] public string? Id { get; set; } + + [JsonProperty(Order = 3)] public string? Type { get; set; } + + [JsonProperty(Order = 4)] public string? Canonical { get; set; } + + [JsonProperty(Order = 10)] public List? SeeAlso { get; set; } + + [JsonProperty(Order = 11)] public List? Provider { get; set; } +} + +public class Actor +{ + [JsonProperty(Order = 2)] public string? Id { get; set; } + + [JsonProperty(Order = 3)] + [EnumAsString] + public ActorType Type { get; set; } +} + +/// +/// Valid values for activity Type +/// +/// See: https://iiif.io/api/discovery/1.0/#type-activity +public enum ActivityType { /// - /// The Activities are the means of describing the changes that have occurred in the content provider’s system. + /// The initial creation of the resource. + /// + Create, + + /// + /// Any change to the resource. + /// + Update, + + /// + /// The deletion of the resource, or its de-publication from the web. + /// + Delete, + + /// + /// The re-publishing of the resource at a new URI, with the same content + /// + Move, + + /// + /// The addition of an object to a stream, outside of any of the above types of activity, such as a third + /// party aggregator adding resources from a newly discovered stream. + /// + Add, + + /// + /// The removal of an object from a stream, outside of any of the above types of activity, such as a third + /// party aggregator removing resources from a stream that are no longer considered to be in scope. /// - /// See https://iiif.io/api/discovery/1.0/#activities - public class Activity : JsonLdBase - { - [JsonProperty(Order = 2)] - public string? Id { get; set; } - - [JsonProperty(Order = 3)] - [EnumAsString] - public ActivityType Type { get; set; } - - /// - /// A short textual description of the Activity - /// - [JsonProperty(Order = 5)] - public string? Summary { get; set; } - - /// - /// The IIIF resource that was affected by the Activity - /// - [JsonProperty(Order = 6)] - public ActivityObject Object { get; set; } - - /// - /// The new location of the IIIF resource, after it was affected by a Move activity. - /// - [JsonProperty(Order = 7)] - public ActivityObject Target { get; set; } - - /// - /// The time at which the Activity was finished. - /// - [JsonProperty(Order = 10)] - [JsonConverter(typeof(XsdDateTimeConverter))] - public DateTime? EndTime { get; set; } - - /// - /// The time at which the Activity was started. - /// - [JsonProperty(Order = 11)] - [JsonConverter(typeof(XsdDateTimeConverter))] - public DateTime? StartTime { get; set; } - - /// - /// The organization, person, or software agent that carried out the Activity. - /// - [JsonProperty(Order = 21)] - public Actor? Actor { get; set; } - } - - public class ActivityObject : IService - { - [JsonProperty(Order = 2)] - public string? Id { get; set; } - - [JsonProperty(Order = 3)] - public string? Type { get; set; } - - [JsonProperty(Order = 4)] - public string? Canonical { get; set; } - - [JsonProperty(Order = 10)] - public List? SeeAlso { get; set; } - - [JsonProperty(Order = 11)] - public List? Provider { get; set; } - } - - public class Actor - { - [JsonProperty(Order = 2)] - public string? Id { get; set; } - - [JsonProperty(Order = 3)] - [EnumAsString] - public ActorType Type { get; set; } - } + Remove, /// - /// Valid values for activity Type + /// The beginning of an activity to refresh the stream with the state of all of the resources. /// - /// See: https://iiif.io/api/discovery/1.0/#type-activity - public enum ActivityType - { - /// - /// The initial creation of the resource. - /// - Create, - - /// - /// Any change to the resource. - /// - Update, - - /// - /// The deletion of the resource, or its de-publication from the web. - /// - Delete, - - /// - /// The re-publishing of the resource at a new URI, with the same content - /// - Move, - - /// - /// The addition of an object to a stream, outside of any of the above types of activity, such as a third - /// party aggregator adding resources from a newly discovered stream. - /// - Add, - - /// - /// The removal of an object from a stream, outside of any of the above types of activity, such as a third - /// party aggregator removing resources from a stream that are no longer considered to be in scope. - /// - Remove, - - /// - /// The beginning of an activity to refresh the stream with the state of all of the resources. - /// - Refresh - } - - public enum ActorType - { - Application, - Organization, - Person - } + Refresh +} + +public enum ActorType +{ + Application, + Organization, + Person } \ No newline at end of file diff --git a/src/IIIF/IIIF/Discovery/V1/OrderedCollection.cs b/src/IIIF/IIIF/Discovery/V1/OrderedCollection.cs index 430f1ef..6b5c4fd 100644 --- a/src/IIIF/IIIF/Discovery/V1/OrderedCollection.cs +++ b/src/IIIF/IIIF/Discovery/V1/OrderedCollection.cs @@ -1,57 +1,53 @@ using System.Collections.Generic; using IIIF.Presentation.V3.Content; -using Newtonsoft.Json; -namespace IIIF.Discovery.V1 +namespace IIIF.Discovery.V1; + +/// +/// The top-most resource for managing the lists of Activities +/// +/// See https://iiif.io/api/discovery/1.0/#orderedcollection +public class OrderedCollection : JsonLdBase, IService { + [JsonProperty(Order = 2)] public string? Id { get; set; } + + [JsonProperty(Order = 3)] public string Type => nameof(OrderedCollection); + + /// + /// The total number of Activities in the entire Ordered Collection. + /// + [JsonProperty(Order = 5)] + public int? TotalItems { get; set; } + + /// + /// A string that identifies a license or rights statement that applies to the usage of the Ordered Collection. + /// + [JsonProperty(Order = 6)] + public string? Rights { get; set; } + + /// + /// Refers to one or more documents that semantically describe the set of resources that are being acted upon in + /// the Activities within the Ordered Collection, rather than any particular resource referenced from within the + /// collection + /// + [JsonProperty(Order = 10)] + public List? SeeAlso { get; set; } + + /// + /// This property is used to refer to a larger Ordered Collection, of which this Ordered Collection is part. + /// + [JsonProperty(Order = 11)] + public List? PartOf { get; set; } + + /// + /// A link to the first Ordered Collection Page for this Collection. + /// + [JsonProperty(Order = 20)] + public OrderedCollectionPage? First { get; set; } + /// - /// The top-most resource for managing the lists of Activities + /// A link to the last Ordered Collection Page for this Collection. /// - /// See https://iiif.io/api/discovery/1.0/#orderedcollection - public class OrderedCollection : JsonLdBase, IService - { - [JsonProperty(Order = 2)] - public string? Id { get; set; } - - [JsonProperty(Order = 3)] - public string Type => nameof(OrderedCollection); - - /// - /// The total number of Activities in the entire Ordered Collection. - /// - [JsonProperty(Order = 5)] - public int? TotalItems { get; set; } - - /// - /// A string that identifies a license or rights statement that applies to the usage of the Ordered Collection. - /// - [JsonProperty(Order = 6)] - public string? Rights { get; set; } - - /// - /// Refers to one or more documents that semantically describe the set of resources that are being acted upon in - /// the Activities within the Ordered Collection, rather than any particular resource referenced from within the - /// collection - /// - [JsonProperty(Order = 10)] - public List? SeeAlso { get; set; } - - /// - /// This property is used to refer to a larger Ordered Collection, of which this Ordered Collection is part. - /// - [JsonProperty(Order = 11)] - public List? PartOf { get; set; } - - /// - /// A link to the first Ordered Collection Page for this Collection. - /// - [JsonProperty(Order = 20)] - public OrderedCollectionPage? First { get; set; } - - /// - /// A link to the last Ordered Collection Page for this Collection. - /// - [JsonProperty(Order = 21)] - public OrderedCollectionPage Last { get; set; } - } + [JsonProperty(Order = 21)] + public OrderedCollectionPage Last { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Discovery/V1/OrderedCollectionPage.cs b/src/IIIF/IIIF/Discovery/V1/OrderedCollectionPage.cs index 1003c73..7b2cd66 100644 --- a/src/IIIF/IIIF/Discovery/V1/OrderedCollectionPage.cs +++ b/src/IIIF/IIIF/Discovery/V1/OrderedCollectionPage.cs @@ -1,49 +1,45 @@ using System.Collections.Generic; -using Newtonsoft.Json; -namespace IIIF.Discovery.V1 +namespace IIIF.Discovery.V1; + +/// +/// A page of Activities +/// +/// See https://iiif.io/api/discovery/1.0/#ordered-collection-page +public class OrderedCollectionPage : JsonLdBase, IService { + [JsonProperty(Order = 2)] public string? Id { get; set; } + + [JsonProperty(Order = 3)] public string Type => nameof(OrderedCollectionPage); + + /// + /// The position of the first item in this page’s orderedItems list, relative to the overall ordering across all + /// pages within the Collection. + /// + [JsonProperty(Order = 10)] + public int? StartIndex { get; set; } + + /// + /// The Ordered Collection of which this Page is a part. + /// + [JsonProperty(Order = 11)] + public OrderedCollection? PartOf { get; set; } + + /// + /// A reference to the previous page in the list of pages. + /// + [JsonProperty(Order = 20)] + public OrderedCollectionPage Prev { get; set; } + + /// + /// A reference to the next page in the list of pages. + /// + [JsonProperty(Order = 21)] + public OrderedCollectionPage? Next { get; set; } + /// - /// A page of Activities + /// The Activities that are listed as part of this page. /// - /// See https://iiif.io/api/discovery/1.0/#ordered-collection-page - public class OrderedCollectionPage : JsonLdBase, IService - { - [JsonProperty(Order = 2)] - public string? Id { get; set; } - - [JsonProperty(Order = 3)] - public string Type => nameof(OrderedCollectionPage); - - /// - /// The position of the first item in this page’s orderedItems list, relative to the overall ordering across all - /// pages within the Collection. - /// - [JsonProperty(Order = 10)] - public int? StartIndex { get; set; } - - /// - /// The Ordered Collection of which this Page is a part. - /// - [JsonProperty(Order = 11)] - public OrderedCollection? PartOf { get; set; } - - /// - /// A reference to the previous page in the list of pages. - /// - [JsonProperty(Order = 20)] - public OrderedCollectionPage Prev { get; set; } - - /// - /// A reference to the next page in the list of pages. - /// - [JsonProperty(Order = 21)] - public OrderedCollectionPage? Next { get; set; } - - /// - /// The Activities that are listed as part of this page. - /// - [JsonProperty(Order = 22)] - public List OrderedItems { get; set; } - } + [JsonProperty(Order = 22)] + public List OrderedItems { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/IIIF.csproj b/src/IIIF/IIIF/IIIF.csproj index 53281ad..6e86e84 100644 --- a/src/IIIF/IIIF/IIIF.csproj +++ b/src/IIIF/IIIF/IIIF.csproj @@ -14,13 +14,13 @@ iiif https://github.com/digirati-co-uk/iiif-net - iiif-net is a collection of POCOs for modelling various IIIF API's and helpers to serialise/deserialise to/from JSON. + iiif-net is a collection of POCOs for modelling various IIIF API's and helpers to serialise/deserialise to/from JSON. - + - - - + + + diff --git a/src/IIIF/IIIF/IService.cs b/src/IIIF/IIIF/IService.cs index a666966..e919532 100644 --- a/src/IIIF/IIIF/IService.cs +++ b/src/IIIF/IIIF/IService.cs @@ -1,8 +1,7 @@ -namespace IIIF +namespace IIIF; + +public interface IService { - public interface IService - { - string? Id { get; set; } - string? Type { get; } - } -} + string? Id { get; set; } + string? Type { get; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/ImageApi/ContentTypes.cs b/src/IIIF/IIIF/ImageApi/ContentTypes.cs index de76282..780115b 100644 --- a/src/IIIF/IIIF/ImageApi/ContentTypes.cs +++ b/src/IIIF/IIIF/ImageApi/ContentTypes.cs @@ -1,21 +1,20 @@ using IIIF.ImageApi.V2; using IIIF.ImageApi.V3; -namespace IIIF.ImageApi +namespace IIIF.ImageApi; + +/// +/// Contains Content-Type/Accepts headers for IIIF Image API. +/// +public static class ContentTypes { /// - /// Contains Content-Type/Accepts headers for IIIF Image API. + /// Content-Type for IIIF Image 2. /// - public static class ContentTypes - { - /// - /// Content-Type for IIIF Image 2. - /// - public const string V2 = "application/ld+json;profile=\"" + ImageService2.Image2Context + "\""; + public const string V2 = "application/ld+json;profile=\"" + ImageService2.Image2Context + "\""; - /// - /// Content-Type for IIIF Image 3. - /// - public const string V3 = "application/ld+json;profile=\"" + ImageService3.Image3Context + "\""; - } + /// + /// Content-Type for IIIF Image 3. + /// + public const string V3 = "application/ld+json;profile=\"" + ImageService3.Image3Context + "\""; } \ No newline at end of file diff --git a/src/IIIF/IIIF/ImageApi/ImageRequest.cs b/src/IIIF/IIIF/ImageApi/ImageRequest.cs index 3b2ad24..ce22863 100644 --- a/src/IIIF/IIIF/ImageApi/ImageRequest.cs +++ b/src/IIIF/IIIF/ImageApi/ImageRequest.cs @@ -1,75 +1,66 @@ using System; -namespace IIIF.ImageApi +namespace IIIF.ImageApi; + +/// +/// Represents a IIIF image request in format: +/// {scheme}://{server}{/prefix}/{identifier}/{region}/{size}/{rotation}/{quality}.{format} +/// +/// See https://iiif.io/api/image/3.0/#21-image-request-uri-syntax +public class ImageRequest { + public string Prefix { get; set; } + public string Identifier { get; set; } + public bool IsBase { get; set; } + public bool IsInformationRequest { get; set; } + public RegionParameter Region { get; set; } + public SizeParameter Size { get; set; } + public RotationParameter Rotation { get; set; } + public string Quality { get; set; } + public string Format { get; set; } + public string OriginalPath { get; set; } + /// - /// Represents a IIIF image request in format: - /// {scheme}://{server}{/prefix}/{identifier}/{region}/{size}/{rotation}/{quality}.{format} + /// Full image request path, e.g. /0,0,400,400/100,/0/default.jpg /// - /// See https://iiif.io/api/image/3.0/#21-image-request-uri-syntax - public class ImageRequest + public string ImageRequestPath => OriginalPath.Replace(Identifier, string.Empty); + + public static ImageRequest Parse(string path, string prefix) { - public string Prefix { get; set; } - public string Identifier { get; set; } - public bool IsBase { get; set; } - public bool IsInformationRequest { get; set; } - public RegionParameter Region { get; set; } - public SizeParameter Size { get; set; } - public RotationParameter Rotation { get; set; } - public string Quality { get; set; } - public string Format { get; set; } - public string OriginalPath { get; set; } - - /// - /// Full image request path, e.g. /0,0,400,400/100,/0/default.jpg - /// - public string ImageRequestPath => OriginalPath.Replace(Identifier, string.Empty); + if (path[0] == '/') path = path[1..]; - public static ImageRequest Parse(string path, string prefix) + if (prefix.Length > 0) { - if (path[0] == '/') - { - path = path[1..]; - } - - if (prefix.Length > 0) - { - if (prefix[0] == '/') - { - prefix = prefix[1..]; - } - if (prefix != path[..prefix.Length]) - { - throw new ArgumentException("Path does not start with prefix", nameof(prefix)); - } - path = path[prefix.Length..]; - } - - var request = new ImageRequest { Prefix = prefix }; - var parts = path.Split('/'); - request.Identifier = parts[0]; - if (parts.Length == 1 || parts[1] == string.Empty) - { - // likely the server will want to redirect this - request.IsBase = true; - return request; - } - - if (parts[1] == "info.json") - { - request.IsInformationRequest = true; - return request; - } + if (prefix[0] == '/') prefix = prefix[1..]; + if (prefix != path[..prefix.Length]) + throw new ArgumentException("Path does not start with prefix", nameof(prefix)); + path = path[prefix.Length..]; + } - request.OriginalPath = path; - request.Region = RegionParameter.Parse(parts[1]); - request.Size = SizeParameter.Parse(parts[2]); - request.Rotation = RotationParameter.Parse(parts[3]); - var filenameParts = parts[4].Split('.'); - request.Quality = filenameParts[0]; - request.Format = filenameParts[1]; + var request = new ImageRequest { Prefix = prefix }; + var parts = path.Split('/'); + request.Identifier = parts[0]; + if (parts.Length == 1 || parts[1] == string.Empty) + { + // likely the server will want to redirect this + request.IsBase = true; + return request; + } + if (parts[1] == "info.json") + { + request.IsInformationRequest = true; return request; } + + request.OriginalPath = path; + request.Region = RegionParameter.Parse(parts[1]); + request.Size = SizeParameter.Parse(parts[2]); + request.Rotation = RotationParameter.Parse(parts[3]); + var filenameParts = parts[4].Split('.'); + request.Quality = filenameParts[0]; + request.Format = filenameParts[1]; + + return request; } -} +} \ No newline at end of file diff --git a/src/IIIF/IIIF/ImageApi/ImageRequestX.cs b/src/IIIF/IIIF/ImageApi/ImageRequestX.cs index aff90b4..d4e7af3 100644 --- a/src/IIIF/IIIF/ImageApi/ImageRequestX.cs +++ b/src/IIIF/IIIF/ImageApi/ImageRequestX.cs @@ -1,39 +1,33 @@ -namespace IIIF.ImageApi +namespace IIIF.ImageApi; + +/// +/// Extension methods for dealing with ImageRequests +/// +public static class ImageRequestX { /// - /// Extension methods for dealing with ImageRequests + /// Resize the original object in accordance with size parameters. + /// This method supports upsizing and always allows upscaling. /// - public static class ImageRequestX + /// Current object + /// + /// of requested resource - this can be original image for /full/ requests or size of tile + /// for tile requests etc + /// + /// + public static Size GetResultingSize(this SizeParameter sizeParameter, Size requestSize) { - /// - /// Resize the original object in accordance with size parameters. - /// This method supports upsizing and always allows upscaling. - /// - /// Current object - /// - /// of requested resource - this can be original image for /full/ requests or size of tile - /// for tile requests etc - /// - /// - public static Size GetResultingSize(this SizeParameter sizeParameter, Size requestSize) - { - if (sizeParameter.Max) - { - return requestSize; - } + if (sizeParameter.Max) return requestSize; - if (sizeParameter.PercentScale.HasValue) - { - return Size.ResizePercent(requestSize, sizeParameter.PercentScale.Value); - } + if (sizeParameter.PercentScale.HasValue) + return Size.ResizePercent(requestSize, sizeParameter.PercentScale.Value); - if (sizeParameter.Width.HasValue && sizeParameter.Height.HasValue && sizeParameter.Confined) - { - var targetSize = new Size(sizeParameter.Width.Value, sizeParameter.Height.Value); - return Size.Confine(targetSize, requestSize); - } - - return Size.Resize(requestSize, sizeParameter.Width, sizeParameter.Height); + if (sizeParameter.Width.HasValue && sizeParameter.Height.HasValue && sizeParameter.Confined) + { + var targetSize = new Size(sizeParameter.Width.Value, sizeParameter.Height.Value); + return Size.Confine(targetSize, requestSize); } + + return Size.Resize(requestSize, sizeParameter.Width, sizeParameter.Height); } } \ No newline at end of file diff --git a/src/IIIF/IIIF/ImageApi/RegionParameter.cs b/src/IIIF/IIIF/ImageApi/RegionParameter.cs index 51738c6..24ef2db 100644 --- a/src/IIIF/IIIF/ImageApi/RegionParameter.cs +++ b/src/IIIF/IIIF/ImageApi/RegionParameter.cs @@ -1,66 +1,58 @@ using System; -using Newtonsoft.Json; -namespace IIIF.ImageApi +namespace IIIF.ImageApi; + +/// +/// Represents the {region} parameter of a IIIF image request. +/// +/// see https://iiif.io/api/image/3.0/#41-region +public class RegionParameter { - /// - /// Represents the {region} parameter of a IIIF image request. - /// - /// see https://iiif.io/api/image/3.0/#41-region - public class RegionParameter - { - [JsonProperty(Order = 91, PropertyName = "x")] - public float X { get; set; } + [JsonProperty(Order = 91, PropertyName = "x")] + public float X { get; set; } - [JsonProperty(Order = 92, PropertyName = "y")] - public float Y { get; set; } + [JsonProperty(Order = 92, PropertyName = "y")] + public float Y { get; set; } - [JsonProperty(Order = 93, PropertyName = "w")] - public float W { get; set; } + [JsonProperty(Order = 93, PropertyName = "w")] + public float W { get; set; } - [JsonProperty(Order = 94, PropertyName = "h")] - public float H { get; set; } + [JsonProperty(Order = 94, PropertyName = "h")] + public float H { get; set; } - public bool Full { get; set; } - public bool Square { get; set; } - public bool Percent { get; set; } + public bool Full { get; set; } + public bool Square { get; set; } + public bool Percent { get; set; } - public override string ToString() - { - if (Full) return "full"; - if (Square) return "square"; - string xywh = $"{X},{Y},{W},{H}"; - if (Percent) return $"pct:{xywh}"; - return xywh; - } + public override string ToString() + { + if (Full) return "full"; + if (Square) return "square"; + var xywh = $"{X},{Y},{W},{H}"; + if (Percent) return $"pct:{xywh}"; + return xywh; + } - public static RegionParameter Parse(string pathPart) + public static RegionParameter Parse(string pathPart) + { + try { - try - { - if (pathPart == "full") - { - return new RegionParameter { Full = true }; - } - if (pathPart == "square") - { - return new RegionParameter { Square = true }; - } + if (pathPart == "full") return new RegionParameter { Full = true }; + if (pathPart == "square") return new RegionParameter { Square = true }; - bool percent = pathPart.StartsWith("pct:"); - var stringParts = pathPart.Substring(percent ? 4 : 0).Split(','); - var xywh = Array.ConvertAll(stringParts, float.Parse); - return new RegionParameter - { - X = xywh[0], Y = xywh[1], W = xywh[2], H = xywh[3], - Percent = percent - }; - } - catch + var percent = pathPart.StartsWith("pct:"); + var stringParts = pathPart.Substring(percent ? 4 : 0).Split(','); + var xywh = Array.ConvertAll(stringParts, float.Parse); + return new RegionParameter { - throw new ArgumentException("Expected 'full', 'square' or 'x,y,w,h'. Found " + pathPart); - } + X = xywh[0], Y = xywh[1], W = xywh[2], H = xywh[3], + Percent = percent + }; + } + catch + { + throw new ArgumentException("Expected 'full', 'square' or 'x,y,w,h'. Found " + pathPart); } } -} +} \ No newline at end of file diff --git a/src/IIIF/IIIF/ImageApi/RotationParameter.cs b/src/IIIF/IIIF/ImageApi/RotationParameter.cs index 1880ad9..16177fb 100644 --- a/src/IIIF/IIIF/ImageApi/RotationParameter.cs +++ b/src/IIIF/IIIF/ImageApi/RotationParameter.cs @@ -1,24 +1,24 @@ -namespace IIIF.ImageApi +namespace IIIF.ImageApi; + +/// +/// Represents the {rotation} parameter of a IIIF image request. +/// +/// see https://iiif.io/api/image/3.0/#43-rotation +public class RotationParameter { - /// - /// Represents the {rotation} parameter of a IIIF image request. - /// - /// see https://iiif.io/api/image/3.0/#43-rotation - public class RotationParameter - { - public bool Mirror { get; set; } - public float Angle { get; set; } + public bool Mirror { get; set; } + public float Angle { get; set; } - public static RotationParameter Parse(string pathPart) + public static RotationParameter Parse(string pathPart) + { + var rotation = new RotationParameter(); + if (pathPart[0] == '!') { - var rotation = new RotationParameter(); - if (pathPart[0] == '!') - { - rotation.Mirror = true; - pathPart = pathPart[1..]; - } - rotation.Angle = float.Parse(pathPart); - return rotation; + rotation.Mirror = true; + pathPart = pathPart[1..]; } + + rotation.Angle = float.Parse(pathPart); + return rotation; } -} +} \ No newline at end of file diff --git a/src/IIIF/IIIF/ImageApi/SizeParameter.cs b/src/IIIF/IIIF/ImageApi/SizeParameter.cs index a38b36e..654156a 100644 --- a/src/IIIF/IIIF/ImageApi/SizeParameter.cs +++ b/src/IIIF/IIIF/ImageApi/SizeParameter.cs @@ -1,99 +1,81 @@ -using System; -using System.Text; +using System.Text; -namespace IIIF.ImageApi +namespace IIIF.ImageApi; + +/// +/// Represents the {size} parameter of a IIIF image request. +/// +/// see https://iiif.io/api/image/3.0/#42-size +public class SizeParameter { - /// - /// Represents the {size} parameter of a IIIF image request. - /// - /// see https://iiif.io/api/image/3.0/#42-size - public class SizeParameter + public int? Width { get; set; } + + public int? Height { get; set; } + + public bool Max { get; set; } + + public bool Upscaled { get; set; } + + public bool Confined { get; set; } + + public float? PercentScale { get; set; } + + public override string ToString() { - public int? Width { get; set; } - - public int? Height { get; set; } - - public bool Max { get; set; } - - public bool Upscaled { get; set; } - - public bool Confined { get; set; } - - public float? PercentScale { get; set; } - - public override string ToString() + var sb = new StringBuilder(); + if (Upscaled) sb.Append('^'); + if (Max) { - var sb = new StringBuilder(); - if (Upscaled) - { - sb.Append('^'); - } - if (Max) - { - sb.Append("max"); - return sb.ToString(); - } - if (Confined) - { - sb.Append('!'); - } - if (PercentScale > 0) - { - sb.Append("pct:" + PercentScale); - return sb.ToString(); - } - if (Width > 0) - { - sb.Append(Width); - } - sb.Append(','); - if (Height > 0) - { - sb.Append(Height); - } + sb.Append("max"); + return sb.ToString(); + } + if (Confined) sb.Append('!'); + if (PercentScale > 0) + { + sb.Append("pct:" + PercentScale); return sb.ToString(); } - public static SizeParameter Parse(string pathPart) + if (Width > 0) sb.Append(Width); + sb.Append(','); + if (Height > 0) sb.Append(Height); + + return sb.ToString(); + } + + public static SizeParameter Parse(string pathPart) + { + var size = new SizeParameter(); + + if (pathPart[0] == '^') + { + size.Upscaled = true; + pathPart = pathPart[1..]; + } + + if (pathPart is "max" or "full") { - var size = new SizeParameter(); - - if (pathPart[0] == '^') - { - size.Upscaled = true; - pathPart = pathPart[1..]; - } - - if (pathPart is "max" or "full") - { - size.Max = true; - return size; - } - - if (pathPart[0] == '!') - { - size.Confined = true; - pathPart = pathPart[1..]; - } - - if (pathPart[0] == 'p') - { - size.PercentScale = float.Parse(pathPart[4..]); - return size; - } - - string[] wh = pathPart.Split(','); - if (wh[0] != string.Empty) - { - size.Width = int.Parse(wh[0]); - } - if (wh[1] != string.Empty) - { - size.Height = int.Parse(wh[1]); - } + size.Max = true; + return size; + } + if (pathPart[0] == '!') + { + size.Confined = true; + pathPart = pathPart[1..]; + } + + if (pathPart[0] == 'p') + { + size.PercentScale = float.Parse(pathPart[4..]); return size; } + + string[] wh = pathPart.Split(','); + if (wh[0] != string.Empty) size.Width = int.Parse(wh[0]); + if (wh[1] != string.Empty) size.Height = int.Parse(wh[1]); + + return size; } -} +} \ No newline at end of file diff --git a/src/IIIF/IIIF/ImageApi/Tile.cs b/src/IIIF/IIIF/ImageApi/Tile.cs index 72831a9..b6bf116 100644 --- a/src/IIIF/IIIF/ImageApi/Tile.cs +++ b/src/IIIF/IIIF/ImageApi/Tile.cs @@ -1,16 +1,10 @@ -using Newtonsoft.Json; +namespace IIIF.ImageApi; -namespace IIIF.ImageApi +public class Tile { - public class Tile - { - [JsonProperty(Order = 1)] - public int Width { get; set; } - - [JsonProperty(Order = 2)] - public int Height { get; set; } - - [JsonProperty(Order = 3)] - public int[] ScaleFactors { get; set; } - } + [JsonProperty(Order = 1)] public int Width { get; set; } + + [JsonProperty(Order = 2)] public int Height { get; set; } + + [JsonProperty(Order = 3)] public int[] ScaleFactors { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/ImageApi/V2/Image2Profile.cs b/src/IIIF/IIIF/ImageApi/V2/Image2Profile.cs index 0329ed7..cd23cb8 100644 --- a/src/IIIF/IIIF/ImageApi/V2/Image2Profile.cs +++ b/src/IIIF/IIIF/ImageApi/V2/Image2Profile.cs @@ -1,9 +1,6 @@ -namespace IIIF.ImageApi.V2 +namespace IIIF.ImageApi.V2; + +// TODO - Full profile object +public class Image2Profile { - // TODO - Full profile object - public class Image2Profile - { - - } - } \ No newline at end of file diff --git a/src/IIIF/IIIF/ImageApi/V2/ImageService2.cs b/src/IIIF/IIIF/ImageApi/V2/ImageService2.cs index 7d7e3b5..6d12c26 100644 --- a/src/IIIF/IIIF/ImageApi/V2/ImageService2.cs +++ b/src/IIIF/IIIF/ImageApi/V2/ImageService2.cs @@ -1,45 +1,37 @@ using System.Collections.Generic; using IIIF.Presentation.V2; using IIIF.Serialisation; -using Newtonsoft.Json; -namespace IIIF.ImageApi.V2 +namespace IIIF.ImageApi.V2; + +public class ImageService2 : ResourceBase, IService { - public class ImageService2 : ResourceBase, IService - { - public const string Image2Context = "http://iiif.io/api/image/2/context.json"; - public const string Level0Profile = "http://iiif.io/api/image/2/level0.json"; - public const string Level1Profile = "http://iiif.io/api/image/2/level1.json"; - public const string Level2Profile = "http://iiif.io/api/image/2/level2.json"; - public const string Image2Protocol = "http://iiif.io/api/image"; - - [JsonProperty(PropertyName = "@type", Order = 3)] - public override string? Type { get; set; } = nameof(ImageService2); - - [JsonProperty(PropertyName = "protocol", Order = 10)] - public string? Protocol { get; set; } - - [JsonIgnore] - public ProfileDescription ProfileDescription { get; set; } - - [JsonProperty(Order = 11)] - public int Width { get; set; } - - [JsonProperty(Order = 12)] - public int Height { get; set; } - - [JsonProperty(Order = 13)] - public List Sizes { get; set; } - - [JsonProperty(Order = 14)] - public List Tiles { get; set; } - - // TODO - Attribution, logo; not needed right now - [JsonProperty(Order = 20)] - public string[] License { get; set; } - - [JsonProperty(Order = 28)] - [ObjectIfSingle] - public List? Service { get; set; } - } + public const string Image2Context = "http://iiif.io/api/image/2/context.json"; + public const string Level0Profile = "http://iiif.io/api/image/2/level0.json"; + public const string Level1Profile = "http://iiif.io/api/image/2/level1.json"; + public const string Level2Profile = "http://iiif.io/api/image/2/level2.json"; + public const string Image2Protocol = "http://iiif.io/api/image"; + + [JsonProperty(PropertyName = "@type", Order = 3)] + public override string? Type { get; set; } = nameof(ImageService2); + + [JsonProperty(PropertyName = "protocol", Order = 10)] + public string? Protocol { get; set; } + + [JsonIgnore] public ProfileDescription ProfileDescription { get; set; } + + [JsonProperty(Order = 11)] public int Width { get; set; } + + [JsonProperty(Order = 12)] public int Height { get; set; } + + [JsonProperty(Order = 13)] public List Sizes { get; set; } + + [JsonProperty(Order = 14)] public List Tiles { get; set; } + + // TODO - Attribution, logo; not needed right now + [JsonProperty(Order = 20)] public string[] License { get; set; } + + [JsonProperty(Order = 28)] + [ObjectIfSingle] + public List? Service { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/ImageApi/V2/ProfileDescription.cs b/src/IIIF/IIIF/ImageApi/V2/ProfileDescription.cs index a0913f7..860b00c 100644 --- a/src/IIIF/IIIF/ImageApi/V2/ProfileDescription.cs +++ b/src/IIIF/IIIF/ImageApi/V2/ProfileDescription.cs @@ -1,61 +1,58 @@ -using Newtonsoft.Json; +namespace IIIF.ImageApi.V2; -namespace IIIF.ImageApi.V2 +/// +/// Specifies additional features that are supported for the image. +/// +/// +public class ProfileDescription : JsonLdBase { + [JsonProperty(PropertyName = "@id", Order = 2)] + public string? Id { get; set; } + + [JsonProperty(PropertyName = "@type", Order = 3)] + public string? Type { get; set; } + + /// + /// The set of image format parameter values available for the image. + /// If not specified then clients should assume only formats declared in the compliance level document. + /// + [JsonProperty(Order = 4, PropertyName = "formats")] + public string[]? Formats { get; set; } + + /// + /// The maximum area in pixels supported for this image. + /// Requests for images sizes with width*height greater than this may not be supported. + /// + [JsonProperty(Order = 5, PropertyName = "maxArea")] + public int? MaxArea { get; set; } + + /// + /// The maximum height in pixels supported for this image. + /// Requests for images sizes with height greater than this may not be supported. + /// If maxWidth is specified and maxHeight is not, then clients should infer that maxHeight = maxWidth. + /// + [JsonProperty(Order = 6, PropertyName = "maxHeight")] + public int? MaxHeight { get; set; } + + /// + /// The maximum width in pixels supported for this image. + /// Requests for images sizes with width greater than this may not be supported. + /// Must be specified if maxHeight is specified. + /// + [JsonProperty(Order = 7, PropertyName = "maxWidth")] + public int? MaxWidth { get; set; } + + /// + /// The set of image quality parameter values available for the image. + /// If not specified then clients should assume only qualities declared in the compliance level document. + /// + [JsonProperty(Order = 8, PropertyName = "qualities")] + public string[]? Qualities { get; set; } + /// - /// Specifies additional features that are supported for the image. + /// The set of features supported for the image. + /// If not specified then clients should assume only features declared in the compliance level document. /// - /// - public class ProfileDescription : JsonLdBase - { - [JsonProperty(PropertyName = "@id", Order = 2)] - public string? Id { get; set; } - - [JsonProperty(PropertyName = "@type", Order = 3)] - public string? Type { get; set; } - - /// - /// The set of image format parameter values available for the image. - /// If not specified then clients should assume only formats declared in the compliance level document. - /// - [JsonProperty(Order = 4, PropertyName = "formats")] - public string[]? Formats { get; set; } - - /// - /// The maximum area in pixels supported for this image. - /// Requests for images sizes with width*height greater than this may not be supported. - /// - [JsonProperty(Order = 5, PropertyName = "maxArea")] - public int? MaxArea { get; set; } - - /// - /// The maximum height in pixels supported for this image. - /// Requests for images sizes with height greater than this may not be supported. - /// If maxWidth is specified and maxHeight is not, then clients should infer that maxHeight = maxWidth. - /// - [JsonProperty(Order = 6, PropertyName = "maxHeight")] - public int? MaxHeight { get; set; } - - /// - /// The maximum width in pixels supported for this image. - /// Requests for images sizes with width greater than this may not be supported. - /// Must be specified if maxHeight is specified. - /// - [JsonProperty(Order = 7, PropertyName = "maxWidth")] - public int? MaxWidth { get; set; } - - /// - /// The set of image quality parameter values available for the image. - /// If not specified then clients should assume only qualities declared in the compliance level document. - /// - [JsonProperty(Order = 8, PropertyName = "qualities")] - public string[]? Qualities { get; set; } - - /// - /// The set of features supported for the image. - /// If not specified then clients should assume only features declared in the compliance level document. - /// - [JsonProperty(Order = 9, PropertyName = "supports")] - public string[]? Supports { get; set; } - } + [JsonProperty(Order = 9, PropertyName = "supports")] + public string[]? Supports { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/ImageApi/V2/Supports.cs b/src/IIIF/IIIF/ImageApi/V2/Supports.cs index 83aa2ee..bf63290 100644 --- a/src/IIIF/IIIF/ImageApi/V2/Supports.cs +++ b/src/IIIF/IIIF/ImageApi/V2/Supports.cs @@ -1,101 +1,100 @@ -namespace IIIF.ImageApi.V2 +namespace IIIF.ImageApi.V2; + +/// +/// Set of features supported by the image +/// +/// +/// See https://iiif.io/api/image/2.1/#profile-description +/// +public class Supports { + /// + /// The base URI of the service will redirect to the image information document. + /// + public const string BaseUriRedirect = "baseUriRedirect"; + + /// + /// The canonical image URI HTTP link header is provided on image responses. + /// + public const string CanonicalLinkHeader = "canonicalLinkHeader"; + + /// + /// The CORS HTTP header is provided on all responses. + /// + public const string Cors = "cors"; + + /// + /// The JSON-LD media type is provided when JSON-LD is requested. + /// + public const string JsonldMediaType = "jsonldMediaType"; + + /// + /// The image may be rotated around the vertical axis, resulting in a left-to-right mirroring of the content. + /// + public const string Mirroring = "mirroring"; + + /// + /// The profile HTTP link header is provided on image responses. + /// + public const string ProfileLinkHeader = "profileLinkHeader"; + + /// + /// Regions of images may be requested by percentage. + /// + public const string RegionByPct = "regionByPct"; + + /// + /// Regions of images may be requested by pixel dimensions. + /// + public const string RegionByPx = "regionByPx"; + + /// + /// A square region where the width and height are equal to the shorter dimension of the complete image content. + /// + public const string RegionSquare = "regionSquare"; + + /// + /// Rotation of images may be requested by degrees other than multiples of 90. + /// + public const string RotationArbitrary = "rotationArbitrary"; + + /// + /// Rotation of images may be requested by degrees in multiples of 90. + /// + public const string RotationBy90s = "rotationBy90s"; + + /// + /// Size of images may be requested larger than the "full" size. See warning. + /// + public const string SizeAboveFull = "sizeAboveFull"; + + /// + /// Size of images may be requested in the form "!w,h". + /// + public const string SizeByConfinedWh = "sizeByConfinedWh"; + + /// + /// Size of images may be requested in the form "w,h", including sizes that would distort the image. + /// + public const string SizeByDistortedWh = "sizeByDistortedWh"; + + /// + /// Size of images may be requested in the form ",h". + /// + public const string SizeByH = "sizeByH"; + + /// + /// Size of images may be requested in the form "pct:n". + /// + public const string SizeByPct = "sizeByPct"; + + /// + /// Size of images may be requested in the form "w,". + /// + public const string SizeByW = "sizeByW"; + /// - /// Set of features supported by the image + /// Size of images may be requested in the form "w,h" where the supplied w and h preserve the aspect ratio. /// - /// - /// See https://iiif.io/api/image/2.1/#profile-description - /// - public class Supports - { - /// - /// The base URI of the service will redirect to the image information document. - /// - public const string BaseUriRedirect = "baseUriRedirect"; - - /// - /// The canonical image URI HTTP link header is provided on image responses. - /// - public const string CanonicalLinkHeader = "canonicalLinkHeader"; - - /// - /// The CORS HTTP header is provided on all responses. - /// - public const string Cors = "cors"; - - /// - /// The JSON-LD media type is provided when JSON-LD is requested. - /// - public const string JsonldMediaType = "jsonldMediaType"; - - /// - /// The image may be rotated around the vertical axis, resulting in a left-to-right mirroring of the content. - /// - public const string Mirroring = "mirroring"; - - /// - /// The profile HTTP link header is provided on image responses. - /// - public const string ProfileLinkHeader = "profileLinkHeader"; - - /// - /// Regions of images may be requested by percentage. - /// - public const string RegionByPct = "regionByPct"; - - /// - /// Regions of images may be requested by pixel dimensions. - /// - public const string RegionByPx = "regionByPx"; - - /// - /// A square region where the width and height are equal to the shorter dimension of the complete image content. - /// - public const string RegionSquare = "regionSquare"; - - /// - /// Rotation of images may be requested by degrees other than multiples of 90. - /// - public const string RotationArbitrary = "rotationArbitrary"; - - /// - /// Rotation of images may be requested by degrees in multiples of 90. - /// - public const string RotationBy90s = "rotationBy90s"; - - /// - /// Size of images may be requested larger than the "full" size. See warning. - /// - public const string SizeAboveFull = "sizeAboveFull"; - - /// - /// Size of images may be requested in the form "!w,h". - /// - public const string SizeByConfinedWh = "sizeByConfinedWh"; - - /// - /// Size of images may be requested in the form "w,h", including sizes that would distort the image. - /// - public const string SizeByDistortedWh = "sizeByDistortedWh"; - - /// - /// Size of images may be requested in the form ",h". - /// - public const string SizeByH = "sizeByH"; - - /// - /// Size of images may be requested in the form "pct:n". - /// - public const string SizeByPct = "sizeByPct"; - - /// - /// Size of images may be requested in the form "w,". - /// - public const string SizeByW = "sizeByW"; - - /// - /// Size of images may be requested in the form "w,h" where the supplied w and h preserve the aspect ratio. - /// - public const string SizeByWh = "sizeByWh"; - } + public const string SizeByWh = "sizeByWh"; } \ No newline at end of file diff --git a/src/IIIF/IIIF/ImageApi/V3/Features.cs b/src/IIIF/IIIF/ImageApi/V3/Features.cs index 69a63e5..0230194 100644 --- a/src/IIIF/IIIF/ImageApi/V3/Features.cs +++ b/src/IIIF/IIIF/ImageApi/V3/Features.cs @@ -1,97 +1,96 @@ -namespace IIIF.ImageApi.V3 +namespace IIIF.ImageApi.V3; + +/// +/// Available features defined for use in the "extraFeatures" property +/// +/// +/// See https://iiif.io/api/image/3.0/#57-extra-functionality +/// +public static class Features { /// - /// Available features defined for use in the "extraFeatures" property - /// - /// - /// See https://iiif.io/api/image/3.0/#57-extra-functionality - /// - public static class Features - { - /// - /// The base URI of the service will redirect to the image information document. - /// - public const string BaseUriRedirect = "baseUriRedirect"; - - /// - /// The canonical image URI HTTP link header is provided on image responses. - /// - public const string CanonicalLinkHeader = "canonicalLinkHeader"; - - /// - /// The CORS HTTP headers are provided on all responses. - /// - public const string Cors = "cors"; - - /// - /// The JSON-LD media type is provided when requested. - /// - public const string JsonldMediaType = "jsonldMediaType"; - - /// - /// The image may be rotated around the vertical axis, resulting in a left-to-right mirroring of the content. - /// - public const string Mirroring = "mirroring"; - - /// - /// The profile HTTP link header is provided on image responses. - /// - public const string ProfileLinkHeader = "profileLinkHeader"; - - /// - /// Regions of the full image may be requested by percentage. - /// - public const string RegionByPct = "regionByPct"; - - /// - /// Regions of the full image may be requested by pixel dimensions. - /// - public const string RegionByPx = "regionByPx"; - - /// - /// A square region may be requested, where the width and height are equal to the shorter dimension of the full - /// image. - /// - public const string RegionSquare = "regionSquare"; - - /// - /// Image rotation may be requested using values other than multiples of 90 degrees. - /// - public const string RotationArbitrary = "rotationArbitrary"; - - /// - /// Image rotation may be requested in multiples of 90 degrees. - /// - public const string RotationBy90s = "rotationBy90s"; - - /// - /// Image size may be requested in the form !w,h. - /// - public const string SizeByConfinedWh = "sizeByConfinedWh"; - - /// - /// Image size may be requested in the form ,h. - /// - public const string SizeByH = "sizeByH"; - - /// - /// Images size may be requested in the form pct:n. - /// - public const string SizeByPct = "sizeByPct"; - - /// - /// Image size may be requested in the form w,. - /// - public const string SizeByW = "sizeByW"; - - /// - /// Image size may be requested in the form w,h. - /// - public const string SizeByWh = "sizeByWh"; - - /// - /// Image sizes prefixed with ^ may be requested. - /// - public const string SizeUpscaling = "sizeUpscaling"; - } + /// The base URI of the service will redirect to the image information document. + /// + public const string BaseUriRedirect = "baseUriRedirect"; + + /// + /// The canonical image URI HTTP link header is provided on image responses. + /// + public const string CanonicalLinkHeader = "canonicalLinkHeader"; + + /// + /// The CORS HTTP headers are provided on all responses. + /// + public const string Cors = "cors"; + + /// + /// The JSON-LD media type is provided when requested. + /// + public const string JsonldMediaType = "jsonldMediaType"; + + /// + /// The image may be rotated around the vertical axis, resulting in a left-to-right mirroring of the content. + /// + public const string Mirroring = "mirroring"; + + /// + /// The profile HTTP link header is provided on image responses. + /// + public const string ProfileLinkHeader = "profileLinkHeader"; + + /// + /// Regions of the full image may be requested by percentage. + /// + public const string RegionByPct = "regionByPct"; + + /// + /// Regions of the full image may be requested by pixel dimensions. + /// + public const string RegionByPx = "regionByPx"; + + /// + /// A square region may be requested, where the width and height are equal to the shorter dimension of the full + /// image. + /// + public const string RegionSquare = "regionSquare"; + + /// + /// Image rotation may be requested using values other than multiples of 90 degrees. + /// + public const string RotationArbitrary = "rotationArbitrary"; + + /// + /// Image rotation may be requested in multiples of 90 degrees. + /// + public const string RotationBy90s = "rotationBy90s"; + + /// + /// Image size may be requested in the form !w,h. + /// + public const string SizeByConfinedWh = "sizeByConfinedWh"; + + /// + /// Image size may be requested in the form ,h. + /// + public const string SizeByH = "sizeByH"; + + /// + /// Images size may be requested in the form pct:n. + /// + public const string SizeByPct = "sizeByPct"; + + /// + /// Image size may be requested in the form w,. + /// + public const string SizeByW = "sizeByW"; + + /// + /// Image size may be requested in the form w,h. + /// + public const string SizeByWh = "sizeByWh"; + + /// + /// Image sizes prefixed with ^ may be requested. + /// + public const string SizeUpscaling = "sizeUpscaling"; } \ No newline at end of file diff --git a/src/IIIF/IIIF/ImageApi/V3/ImageService3.cs b/src/IIIF/IIIF/ImageApi/V3/ImageService3.cs index 004d000..71350ee 100644 --- a/src/IIIF/IIIF/ImageApi/V3/ImageService3.cs +++ b/src/IIIF/IIIF/ImageApi/V3/ImageService3.cs @@ -1,55 +1,53 @@ using System.Collections.Generic; using IIIF.Presentation.V3; -using Newtonsoft.Json; -namespace IIIF.ImageApi.V3 +namespace IIIF.ImageApi.V3; + +public class ImageService3 : ResourceBase { - public class ImageService3 : ResourceBase - { - public const string Image3Context = "http://iiif.io/api/image/3/context.json"; - public const string Level0Profile = "level0"; - public const string Level1Profile = "level1"; - public const string Level2Profile = "level2"; - public const string ImageProtocol = "http://iiif.io/api/image"; - - public override string Type => nameof(ImageService3); - - [JsonProperty(Order = 10)] public string Protocol { get; set; } - - [JsonProperty(Order = 11)] public int Width { get; set; } - - [JsonProperty(Order = 12)] public int Height { get; set; } - - [JsonProperty(Order = 13)] public List Sizes { get; set; } - - [JsonProperty(Order = 14)] public List Tiles { get; set; } - - /// - /// An array of strings that are the preferred format parameter values, arranged in order of preference. The - /// format parameter values listed must be among those specified in the referenced profile or listed in the - /// extraFormats property - /// - [JsonProperty(Order = 26)] - public List PreferredFormats { get; set; } - - /// - /// An array of strings that can be used as the quality parameter, in addition to default. - /// - [JsonProperty(Order = 28)] - public List ExtraQualities { get; set; } - - /// - /// An array of strings that can be used as the format parameter, in addition to the ones specified in the - /// referenced profile. - /// - [JsonProperty(Order = 29)] - public List ExtraFormats { get; set; } - - /// - /// An array of strings identifying features supported by the service, in addition to the ones specified in the - /// referenced profile. - /// - [JsonProperty(Order = 30)] - public List ExtraFeatures { get; set; } - } -} + public const string Image3Context = "http://iiif.io/api/image/3/context.json"; + public const string Level0Profile = "level0"; + public const string Level1Profile = "level1"; + public const string Level2Profile = "level2"; + public const string ImageProtocol = "http://iiif.io/api/image"; + + public override string Type => nameof(ImageService3); + + [JsonProperty(Order = 10)] public string Protocol { get; set; } + + [JsonProperty(Order = 11)] public int Width { get; set; } + + [JsonProperty(Order = 12)] public int Height { get; set; } + + [JsonProperty(Order = 13)] public List Sizes { get; set; } + + [JsonProperty(Order = 14)] public List Tiles { get; set; } + + /// + /// An array of strings that are the preferred format parameter values, arranged in order of preference. The + /// format parameter values listed must be among those specified in the referenced profile or listed in the + /// extraFormats property + /// + [JsonProperty(Order = 26)] + public List PreferredFormats { get; set; } + + /// + /// An array of strings that can be used as the quality parameter, in addition to default. + /// + [JsonProperty(Order = 28)] + public List ExtraQualities { get; set; } + + /// + /// An array of strings that can be used as the format parameter, in addition to the ones specified in the + /// referenced profile. + /// + [JsonProperty(Order = 29)] + public List ExtraFormats { get; set; } + + /// + /// An array of strings identifying features supported by the service, in addition to the ones specified in the + /// referenced profile. + /// + [JsonProperty(Order = 30)] + public List ExtraFeatures { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/ImageApi/Version.cs b/src/IIIF/IIIF/ImageApi/Version.cs index e6a3def..426c3b4 100644 --- a/src/IIIF/IIIF/ImageApi/Version.cs +++ b/src/IIIF/IIIF/ImageApi/Version.cs @@ -2,29 +2,27 @@ using IIIF.ImageApi.V2; using IIIF.ImageApi.V3; -namespace IIIF.ImageApi +namespace IIIF.ImageApi; + +/// +/// Available IIIF Image API Versions. +/// +public enum Version { /// - /// Available IIIF Image API Versions. + /// Fallback value, unknown version. + /// + [Display(Description = "Unknown")] Unknown = 0, + + /// + /// IIIF Image API version 2. + /// + [Display(Description = ImageService2.Image2Context)] + V2, + + /// + /// IIIF Image API version 3. /// - public enum Version - { - /// - /// Fallback value, unknown version. - /// - [Display(Description = "Unknown")] - Unknown = 0, - - /// - /// IIIF Image API version 2. - /// - [Display(Description = ImageService2.Image2Context)] - V2, - - /// - /// IIIF Image API version 3. - /// - [Display(Description = ImageService3.Image3Context)] - V3 - } + [Display(Description = ImageService3.Image3Context)] + V3 } \ No newline at end of file diff --git a/src/IIIF/IIIF/JsonLdBase.cs b/src/IIIF/IIIF/JsonLdBase.cs index 055841a..b565a06 100644 --- a/src/IIIF/IIIF/JsonLdBase.cs +++ b/src/IIIF/IIIF/JsonLdBase.cs @@ -1,14 +1,10 @@ -using Newtonsoft.Json; +namespace IIIF; -namespace IIIF +/// +/// Base class, serves as root for all IIIF models. +/// +public abstract class JsonLdBase { - /// - /// Base class, serves as root for all IIIF models. - /// - public abstract class JsonLdBase - { - // TODO - this can be List or string - how will deserializer handle this? string[] or string? - [JsonProperty(Order = 1, PropertyName = "@context")] - public object? Context { get; set; } // This one needs its serialisation name changing... - } -} + [JsonProperty(Order = 1, PropertyName = "@context")] + public object? Context { get; set; } // This one needs its serialisation name changing... +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/ContentTypes.cs b/src/IIIF/IIIF/Presentation/ContentTypes.cs index b460b34..55bde91 100644 --- a/src/IIIF/IIIF/Presentation/ContentTypes.cs +++ b/src/IIIF/IIIF/Presentation/ContentTypes.cs @@ -1,18 +1,17 @@ -namespace IIIF.Presentation +namespace IIIF.Presentation; + +/// +/// Contains Content-Type/Accepts headers for IIIF Presentation API. +/// +public static class ContentTypes { /// - /// Contains Content-Type/Accepts headers for IIIF Presentation API. + /// Content-Type for IIIF presentation 2. /// - public static class ContentTypes - { - /// - /// Content-Type for IIIF presentation 2. - /// - public const string V2 = "application/ld+json;profile=\"" + Context.Presentation2Context + "\""; + public const string V2 = "application/ld+json;profile=\"" + Context.Presentation2Context + "\""; - /// - /// Content-Type for IIIF presentation 3. - /// - public const string V3 = "application/ld+json;profile=\"" + Context.Presentation3Context + "\""; - } + /// + /// Content-Type for IIIF presentation 3. + /// + public const string V3 = "application/ld+json;profile=\"" + Context.Presentation3Context + "\""; } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/Context.cs b/src/IIIF/IIIF/Presentation/Context.cs index 89b7915..c394e52 100644 --- a/src/IIIF/IIIF/Presentation/Context.cs +++ b/src/IIIF/IIIF/Presentation/Context.cs @@ -1,104 +1,81 @@ using System; using System.Collections.Generic; -namespace IIIF.Presentation +namespace IIIF.Presentation; + +/// +/// Contains JSON-LD Contexts for IIIF Presentation API. +/// +public static class Context { /// - /// Contains JSON-LD Contexts for IIIF Presentation API. + /// JSON-LD context for IIIF presentation 2. + /// + public const string Presentation2Context = "http://iiif.io/api/presentation/2/context.json"; + + /// + /// JSON-LD context for IIIF presentation 3. /// - public static class Context + public const string Presentation3Context = "http://iiif.io/api/presentation/3/context.json"; + + public static void EnsurePresentation3Context(this JsonLdBase resource) { - /// - /// JSON-LD context for IIIF presentation 2. - /// - public const string Presentation2Context = "http://iiif.io/api/presentation/2/context.json"; + resource.EnsureContext(Presentation3Context); + } - /// - /// JSON-LD context for IIIF presentation 3. - /// - public const string Presentation3Context = "http://iiif.io/api/presentation/3/context.json"; + public static void EnsurePresentation2Context(this JsonLdBase resource) + { + resource.EnsureContext(Presentation2Context); + } - public static void EnsurePresentation3Context(this JsonLdBase resource) - { - resource.EnsureContext(Presentation3Context); - } - - public static void EnsurePresentation2Context(this JsonLdBase resource) + // The IIIF context must be last in the list, to override any that come before it. + public static void EnsureContext(this JsonLdBase resource, string contextToEnsure) + { + if (resource.Context == null) { - resource.EnsureContext(Presentation2Context); + resource.Context = contextToEnsure; + return; } - // The IIIF context must be last in the list, to override any that come before it. - public static void EnsureContext(this JsonLdBase resource, string contextToEnsure) - { - if (resource.Context == null) - { - resource.Context = contextToEnsure; - return; - } + List workingContexts = new(); + if (resource.Context is List existingContexts) workingContexts = existingContexts; - List workingContexts = new(); - if (resource.Context is List existingContexts) - { - workingContexts = existingContexts; - } + if (resource.Context is string singleContext) workingContexts = new List { singleContext }; - if (resource.Context is string singleContext) + List newContexts = new(); + var requiresPresentation3Context = contextToEnsure == Presentation3Context; + var requiresPresentation2Context = contextToEnsure == Presentation2Context; + foreach (var workingContext in workingContexts) + switch (workingContext) { - workingContexts = new List { singleContext }; - } - - List newContexts = new(); - bool requiresPresentation3Context = contextToEnsure == Presentation3Context; - bool requiresPresentation2Context = contextToEnsure == Presentation2Context; - foreach (string workingContext in workingContexts) - { - switch (workingContext) - { - case Presentation3Context: - requiresPresentation3Context = true; - break; - case Presentation2Context: - requiresPresentation2Context = true; - break; - default: - newContexts.Add(workingContext); - break; - } + case Presentation3Context: + requiresPresentation3Context = true; + break; + case Presentation2Context: + requiresPresentation2Context = true; + break; + default: + newContexts.Add(workingContext); + break; } - // Add the new context to the list but not if it supposed to come last - if (!newContexts.Contains(contextToEnsure) - && contextToEnsure != Presentation3Context - && contextToEnsure != Presentation2Context) - { - newContexts.Add(contextToEnsure); - } + // Add the new context to the list but not if it supposed to come last + if (!newContexts.Contains(contextToEnsure) + && contextToEnsure != Presentation3Context + && contextToEnsure != Presentation2Context) + newContexts.Add(contextToEnsure); - if (requiresPresentation2Context && requiresPresentation3Context) - { - throw new InvalidOperationException( - "You cannot have Presentation 2 and Presentation 3 contexts in the same resource."); - } - // These have to come last - if (requiresPresentation3Context) - { - newContexts.Add(Presentation3Context); - } - if (requiresPresentation2Context) - { - newContexts.Add(Presentation2Context); - } + if (requiresPresentation2Context && requiresPresentation3Context) + throw new InvalidOperationException( + "You cannot have Presentation 2 and Presentation 3 contexts in the same resource."); + // These have to come last + if (requiresPresentation3Context) newContexts.Add(Presentation3Context); + if (requiresPresentation2Context) newContexts.Add(Presentation2Context); - // Now JSON-LD rules. The @context is the only Presentation 3 element that has this. - if (newContexts.Count == 1) - { - resource.Context = newContexts[0]; - } - else - { - resource.Context = newContexts; - } - } + // Now JSON-LD rules. The @context is the only Presentation 3 element that has this. + if (newContexts.Count == 1) + resource.Context = newContexts[0]; + else + resource.Context = newContexts; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/Annotation/Annotation.cs b/src/IIIF/IIIF/Presentation/V2/Annotation/Annotation.cs index d3c9f87..d039eaa 100644 --- a/src/IIIF/IIIF/Presentation/V2/Annotation/Annotation.cs +++ b/src/IIIF/IIIF/Presentation/V2/Annotation/Annotation.cs @@ -1,28 +1,27 @@ using IIIF.Presentation.V3.Annotation; using Newtonsoft.Json; -namespace IIIF.Presentation.V2.Annotation +namespace IIIF.Presentation.V2.Annotation; + +public class Annotation : ResourceBase, IAnnotation { - public class Annotation : ResourceBase, IAnnotation - { - [JsonProperty(Order = 10, PropertyName = "motivation")] - public virtual string Motivation { get; set; } + [JsonProperty(Order = 10, PropertyName = "motivation")] + public virtual string Motivation { get; set; } - public override string? Type { get; set; } = "oa:Annotation"; + public override string? Type { get; set; } = "oa:Annotation"; - [JsonProperty(Order = 40, PropertyName = "resource")] - public ResourceBase Resource { get; set; } + [JsonProperty(Order = 40, PropertyName = "resource")] + public ResourceBase Resource { get; set; } - // TODO - on can be an object with an @id and a "within" as well as a URI - // "on" : { - // "@id": "http://example.org/identifier/canvas1#xywh=100,100,250,20", - // "within": { - // "@id": "http://example.org/identifier/manifest", - // "type": "sc:Manifest", - // "label": "Example Manifest" - // } - // } - [JsonProperty(Order = 50, PropertyName = "on")] - public string On { get; set; } - } + // TODO - on can be an object with an @id and a "within" as well as a URI + // "on" : { + // "@id": "http://example.org/identifier/canvas1#xywh=100,100,250,20", + // "within": { + // "@id": "http://example.org/identifier/manifest", + // "type": "sc:Manifest", + // "label": "Example Manifest" + // } + // } + [JsonProperty(Order = 50, PropertyName = "on")] + public string On { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/Annotation/AnnotationList.cs b/src/IIIF/IIIF/Presentation/V2/Annotation/AnnotationList.cs index d69dbd9..18185cf 100644 --- a/src/IIIF/IIIF/Presentation/V2/Annotation/AnnotationList.cs +++ b/src/IIIF/IIIF/Presentation/V2/Annotation/AnnotationList.cs @@ -3,14 +3,13 @@ using IIIF.Serialisation; using Newtonsoft.Json; -namespace IIIF.Presentation.V2.Annotation +namespace IIIF.Presentation.V2.Annotation; + +public class AnnotationList : ResourceBase { - public class AnnotationList : ResourceBase - { - public override string? Type { get; set; } = "sc:AnnotationList"; + public override string? Type { get; set; } = "sc:AnnotationList"; - [JsonProperty(Order = 20, PropertyName = "resources")] - [RequiredOutput] - public List Resources { get; set; } - } -} + [JsonProperty(Order = 20, PropertyName = "resources")] + [RequiredOutput] + public List Resources { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/Annotation/AnnotationListReference.cs b/src/IIIF/IIIF/Presentation/V2/Annotation/AnnotationListReference.cs index d1387a9..39962e0 100644 --- a/src/IIIF/IIIF/Presentation/V2/Annotation/AnnotationListReference.cs +++ b/src/IIIF/IIIF/Presentation/V2/Annotation/AnnotationListReference.cs @@ -1,17 +1,16 @@ using IIIF.Presentation.V2.Strings; using Newtonsoft.Json; -namespace IIIF.Presentation.V2.Annotation +namespace IIIF.Presentation.V2.Annotation; + +public class AnnotationListReference : ResourceBase, IAnnotationListReference { - public class AnnotationListReference : ResourceBase, IAnnotationListReference + public override string? Type { - public override string? Type - { - get => "sc:AnnotationList"; - set => throw new System.NotImplementedException(); - } - - [JsonProperty(Order = 40, PropertyName = "label")] - public MetaDataValue? Label { get; set; } + get => "sc:AnnotationList"; + set => throw new System.NotImplementedException(); } + + [JsonProperty(Order = 40, PropertyName = "label")] + public MetaDataValue? Label { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/Annotation/ContentAsTextAnnotationResource.cs b/src/IIIF/IIIF/Presentation/V2/Annotation/ContentAsTextAnnotationResource.cs index ad7363c..47d174b 100644 --- a/src/IIIF/IIIF/Presentation/V2/Annotation/ContentAsTextAnnotationResource.cs +++ b/src/IIIF/IIIF/Presentation/V2/Annotation/ContentAsTextAnnotationResource.cs @@ -1,19 +1,18 @@ using Newtonsoft.Json; -namespace IIIF.Presentation.V2.Annotation +namespace IIIF.Presentation.V2.Annotation; + +public class ContentAsTextAnnotationResource : ResourceBase { - public class ContentAsTextAnnotationResource : ResourceBase + public override string? Type { - public override string? Type - { - get => "cnt:ContentAsText"; - set => throw new System.NotImplementedException(); - } + get => "cnt:ContentAsText"; + set => throw new System.NotImplementedException(); + } - [JsonProperty(Order = 10, PropertyName = "format")] - public string Format { get; set; } + [JsonProperty(Order = 10, PropertyName = "format")] + public string Format { get; set; } - [JsonProperty(Order = 20, PropertyName = "chars")] - public string Chars { get; set; } - } + [JsonProperty(Order = 20, PropertyName = "chars")] + public string Chars { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/Annotation/IAnnotationListReference.cs b/src/IIIF/IIIF/Presentation/V2/Annotation/IAnnotationListReference.cs index 1757da2..059e686 100644 --- a/src/IIIF/IIIF/Presentation/V2/Annotation/IAnnotationListReference.cs +++ b/src/IIIF/IIIF/Presentation/V2/Annotation/IAnnotationListReference.cs @@ -1,7 +1,8 @@ -namespace IIIF.Presentation.V2.Annotation +namespace IIIF.Presentation.V2.Annotation; + +/// +/// Marker interface for otherContent +/// +public interface IAnnotationListReference { - /// - /// Marker interface for otherContent - /// - public interface IAnnotationListReference{} } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/Annotation/IllustrationAnnotationResource.cs b/src/IIIF/IIIF/Presentation/V2/Annotation/IllustrationAnnotationResource.cs index 6ac1e55..459e343 100644 --- a/src/IIIF/IIIF/Presentation/V2/Annotation/IllustrationAnnotationResource.cs +++ b/src/IIIF/IIIF/Presentation/V2/Annotation/IllustrationAnnotationResource.cs @@ -1,7 +1,6 @@ -namespace IIIF.Presentation.V2.Annotation +namespace IIIF.Presentation.V2.Annotation; + +public class IllustrationAnnotationResource : ResourceBase { - public class IllustrationAnnotationResource : ResourceBase - { - public override string? Type { get; set; } - } + public override string? Type { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/Annotation/ImageAnnotation.cs b/src/IIIF/IIIF/Presentation/V2/Annotation/ImageAnnotation.cs index ac52769..1cdf6b4 100644 --- a/src/IIIF/IIIF/Presentation/V2/Annotation/ImageAnnotation.cs +++ b/src/IIIF/IIIF/Presentation/V2/Annotation/ImageAnnotation.cs @@ -1,22 +1,21 @@ using Newtonsoft.Json; -namespace IIIF.Presentation.V2.Annotation +namespace IIIF.Presentation.V2.Annotation; + +public class ImageAnnotation : ResourceBase { - public class ImageAnnotation : ResourceBase + public override string? Type { - public override string? Type - { - get => "oa:Annotation"; - set => throw new System.NotImplementedException(); - } + get => "oa:Annotation"; + set => throw new System.NotImplementedException(); + } - [JsonProperty(Order = 4, PropertyName = "motivation")] - public string Motivation => "sc:painting"; + [JsonProperty(Order = 4, PropertyName = "motivation")] + public string Motivation => "sc:painting"; - [JsonProperty(Order = 10, PropertyName = "resource")] - public ImageResource Resource { get; set; } + [JsonProperty(Order = 10, PropertyName = "resource")] + public ImageResource Resource { get; set; } - [JsonProperty(Order = 36, PropertyName = "on")] - public string On { get; set; } - } + [JsonProperty(Order = 36, PropertyName = "on")] + public string On { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/Annotation/SearchResultAnnotation.cs b/src/IIIF/IIIF/Presentation/V2/Annotation/SearchResultAnnotation.cs index 7f946f7..6817f6d 100644 --- a/src/IIIF/IIIF/Presentation/V2/Annotation/SearchResultAnnotation.cs +++ b/src/IIIF/IIIF/Presentation/V2/Annotation/SearchResultAnnotation.cs @@ -1,7 +1,6 @@ -namespace IIIF.Presentation.V2.Annotation +namespace IIIF.Presentation.V2.Annotation; + +public class SearchResultAnnotation : Annotation { - public class SearchResultAnnotation : Annotation - { - public override string Motivation => "sc:painting"; - } + public override string Motivation => "sc:painting"; } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/Annotation/SearchResultAnnotationResource.cs b/src/IIIF/IIIF/Presentation/V2/Annotation/SearchResultAnnotationResource.cs index 1b3f1db..2c54dd2 100644 --- a/src/IIIF/IIIF/Presentation/V2/Annotation/SearchResultAnnotationResource.cs +++ b/src/IIIF/IIIF/Presentation/V2/Annotation/SearchResultAnnotationResource.cs @@ -1,16 +1,15 @@ using Newtonsoft.Json; -namespace IIIF.Presentation.V2.Annotation +namespace IIIF.Presentation.V2.Annotation; + +public class SearchResultAnnotationResource : ResourceBase { - public class SearchResultAnnotationResource : ResourceBase + public override string? Type { - public override string? Type - { - get => "cnt:ContentAsText"; - set => throw new System.NotImplementedException(); - } - - [JsonProperty(Order = 10, PropertyName = "chars")] - public string? Chars { get; set; } + get => "cnt:ContentAsText"; + set => throw new System.NotImplementedException(); } + + [JsonProperty(Order = 10, PropertyName = "chars")] + public string? Chars { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/Canvas.cs b/src/IIIF/IIIF/Presentation/V2/Canvas.cs index ec686a5..5a89456 100644 --- a/src/IIIF/IIIF/Presentation/V2/Canvas.cs +++ b/src/IIIF/IIIF/Presentation/V2/Canvas.cs @@ -2,24 +2,23 @@ using IIIF.Presentation.V2.Annotation; using Newtonsoft.Json; -namespace IIIF.Presentation.V2 +namespace IIIF.Presentation.V2; + +public class Canvas : IIIFPresentationBase { - public class Canvas : IIIFPresentationBase + public override string? Type { - public override string? Type - { - get => "sc:Canvas"; - set => throw new System.NotImplementedException(); - } + get => "sc:Canvas"; + set => throw new System.NotImplementedException(); + } - [JsonProperty(Order = 35, PropertyName = "height")] - public int? Height { get; set; } + [JsonProperty(Order = 35, PropertyName = "height")] + public int? Height { get; set; } - [JsonProperty(Order = 36, PropertyName = "width")] - public int? Width { get; set; } + [JsonProperty(Order = 36, PropertyName = "width")] + public int? Width { get; set; } - // Link to Image resources - [JsonProperty(Order = 60, PropertyName = "images")] - public List Images { get; set; } - } + // Link to Image resources + [JsonProperty(Order = 60, PropertyName = "images")] + public List Images { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/Collection.cs b/src/IIIF/IIIF/Presentation/V2/Collection.cs index 194d9ce..675501f 100644 --- a/src/IIIF/IIIF/Presentation/V2/Collection.cs +++ b/src/IIIF/IIIF/Presentation/V2/Collection.cs @@ -1,28 +1,27 @@ using System.Collections.Generic; using Newtonsoft.Json; -namespace IIIF.Presentation.V2 +namespace IIIF.Presentation.V2; + +/// +/// Collections are used to list the manifests available for viewing, and to describe the structures, hierarchies +/// or curated collections that the physical objects are part of. +/// +/// See: https://iiif.io/api/presentation/2.1/#collection +public class Collection : IIIFPresentationBase { - /// - /// Collections are used to list the manifests available for viewing, and to describe the structures, hierarchies - /// or curated collections that the physical objects are part of. - /// - /// See: https://iiif.io/api/presentation/2.1/#collection - public class Collection : IIIFPresentationBase + public override string? Type { - public override string? Type - { - get => "sc:Collection"; - set => throw new System.NotImplementedException(); - } + get => "sc:Collection"; + set => throw new System.NotImplementedException(); + } - [JsonProperty(Order = 100, PropertyName = "collections")] - public List Collections { get; set; } + [JsonProperty(Order = 100, PropertyName = "collections")] + public List Collections { get; set; } - [JsonProperty(Order = 101, PropertyName = "manifests")] - public List Manifests { get; set; } + [JsonProperty(Order = 101, PropertyName = "manifests")] + public List Manifests { get; set; } - [JsonProperty(Order = 111, PropertyName = "members")] - public List Members { get; set; } - } + [JsonProperty(Order = 111, PropertyName = "members")] + public List Members { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/ExternalResource.cs b/src/IIIF/IIIF/Presentation/V2/ExternalResource.cs index 195adba..d26fcb4 100644 --- a/src/IIIF/IIIF/Presentation/V2/ExternalResource.cs +++ b/src/IIIF/IIIF/Presentation/V2/ExternalResource.cs @@ -1,17 +1,16 @@ using IIIF.Presentation.V2.Strings; using Newtonsoft.Json; -namespace IIIF.Presentation.V2 +namespace IIIF.Presentation.V2; + +public class ExternalResource { - public class ExternalResource - { - [JsonProperty(Order = 1, PropertyName = "@id")] - public string? Id { get; set; } - - [JsonProperty(Order = 2, PropertyName = "label")] - public MetaDataValue? Label { get; set; } - - [JsonProperty(Order = 3, PropertyName = "format")] - public string? Format { get; set; } - } + [JsonProperty(Order = 1, PropertyName = "@id")] + public string? Id { get; set; } + + [JsonProperty(Order = 2, PropertyName = "label")] + public MetaDataValue? Label { get; set; } + + [JsonProperty(Order = 3, PropertyName = "format")] + public string? Format { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/IIIFPresentationBase.cs b/src/IIIF/IIIF/Presentation/V2/IIIFPresentationBase.cs index 42745b3..a391187 100644 --- a/src/IIIF/IIIF/Presentation/V2/IIIFPresentationBase.cs +++ b/src/IIIF/IIIF/Presentation/V2/IIIFPresentationBase.cs @@ -4,55 +4,54 @@ using IIIF.Serialisation; using Newtonsoft.Json; -namespace IIIF.Presentation.V2 +namespace IIIF.Presentation.V2; + +/// +/// Base class, used as root to all IIIF v2 Presentation models. +/// +public abstract class IIIFPresentationBase : ResourceBase { - /// - /// Base class, used as root to all IIIF v2 Presentation models. - /// - public abstract class IIIFPresentationBase : ResourceBase - { - [JsonProperty(Order = 12, PropertyName = "metadata")] - public List? Metadata { get; set; } - - [JsonProperty(Order = 15, PropertyName = "thumbnail")] - [ObjectIfSingle] - public List? Thumbnail { get; set; } - - [JsonProperty(Order = 16, PropertyName = "attribution")] - public MetaDataValue? Attribution { get; set; } - - [JsonProperty(Order = 17, PropertyName = "license")] - public string? License { get; set; } - - [JsonProperty(Order = 18, PropertyName = "logo")] - public string? Logo { get; set; } - - [JsonProperty(Order = 24, PropertyName = "rendering")] - [ObjectIfSingle] - public List? Rendering { get; set; } - - [JsonProperty(Order = 25, PropertyName = "related")] - [ObjectIfSingle] - public List? Related { get; set; } - - [JsonProperty(Order = 26, PropertyName = "seeAlso")] - [ObjectIfSingle] - public List? SeeAlso { get; set; } - - [JsonProperty(Order = 27, PropertyName = "service")] - [ObjectIfSingle] - public List? Service { get; set; } - - [JsonProperty(Order = 30, PropertyName = "viewingHint")] - public string? ViewingHint { get; set; } - - [JsonProperty(Order = 32, PropertyName = "navDate")] - public string? NavDate { get; set; } - - [JsonProperty(Order = 60, PropertyName = "otherContent")] - public List? OtherContent { get; set; } - - [JsonProperty(Order = 70, PropertyName = "within")] - public string? Within { get; set; } - } + [JsonProperty(Order = 12, PropertyName = "metadata")] + public List? Metadata { get; set; } + + [JsonProperty(Order = 15, PropertyName = "thumbnail")] + [ObjectIfSingle] + public List? Thumbnail { get; set; } + + [JsonProperty(Order = 16, PropertyName = "attribution")] + public MetaDataValue? Attribution { get; set; } + + [JsonProperty(Order = 17, PropertyName = "license")] + public string? License { get; set; } + + [JsonProperty(Order = 18, PropertyName = "logo")] + public string? Logo { get; set; } + + [JsonProperty(Order = 24, PropertyName = "rendering")] + [ObjectIfSingle] + public List? Rendering { get; set; } + + [JsonProperty(Order = 25, PropertyName = "related")] + [ObjectIfSingle] + public List? Related { get; set; } + + [JsonProperty(Order = 26, PropertyName = "seeAlso")] + [ObjectIfSingle] + public List? SeeAlso { get; set; } + + [JsonProperty(Order = 27, PropertyName = "service")] + [ObjectIfSingle] + public List? Service { get; set; } + + [JsonProperty(Order = 30, PropertyName = "viewingHint")] + public string? ViewingHint { get; set; } + + [JsonProperty(Order = 32, PropertyName = "navDate")] + public string? NavDate { get; set; } + + [JsonProperty(Order = 60, PropertyName = "otherContent")] + public List? OtherContent { get; set; } + + [JsonProperty(Order = 70, PropertyName = "within")] + public string? Within { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/ImageResource.cs b/src/IIIF/IIIF/Presentation/V2/ImageResource.cs index 0b8ae94..d5c1bfa 100644 --- a/src/IIIF/IIIF/Presentation/V2/ImageResource.cs +++ b/src/IIIF/IIIF/Presentation/V2/ImageResource.cs @@ -1,19 +1,18 @@ using Newtonsoft.Json; -namespace IIIF.Presentation.V2 +namespace IIIF.Presentation.V2; + +public class ImageResource : Resource { - public class ImageResource : Resource + public override string? Type { - public override string? Type - { - get => "dctypes:Image"; - set => throw new System.NotImplementedException(); - } + get => "dctypes:Image"; + set => throw new System.NotImplementedException(); + } - [JsonProperty(Order = 35, PropertyName = "height")] - public int? Height { get; set; } + [JsonProperty(Order = 35, PropertyName = "height")] + public int? Height { get; set; } - [JsonProperty(Order = 36, PropertyName = "width")] - public int? Width { get; set; } - } + [JsonProperty(Order = 36, PropertyName = "width")] + public int? Width { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/Manifest.cs b/src/IIIF/IIIF/Presentation/V2/Manifest.cs index dd74816..9cb3c74 100644 --- a/src/IIIF/IIIF/Presentation/V2/Manifest.cs +++ b/src/IIIF/IIIF/Presentation/V2/Manifest.cs @@ -1,28 +1,27 @@ using System.Collections.Generic; using Newtonsoft.Json; -namespace IIIF.Presentation.V2 +namespace IIIF.Presentation.V2; + +/// +/// The manifest response contains sufficient information for the client to initialize itself and begin to display +/// something quickly to the user. +/// +/// See https://iiif.io/api/presentation/2.1/#manifest +public class Manifest : IIIFPresentationBase { - /// - /// The manifest response contains sufficient information for the client to initialize itself and begin to display - /// something quickly to the user. - /// - /// See https://iiif.io/api/presentation/2.1/#manifest - public class Manifest : IIIFPresentationBase + public override string? Type { - public override string? Type - { - get => "sc:Manifest"; - set => throw new System.NotImplementedException(); - } + get => "sc:Manifest"; + set => throw new System.NotImplementedException(); + } - [JsonProperty(Order = 31, PropertyName = "viewingDirection")] - public string? ViewingDirection { get; set; } + [JsonProperty(Order = 31, PropertyName = "viewingDirection")] + public string? ViewingDirection { get; set; } - [JsonProperty(Order = 40, PropertyName = "sequences")] - public List Sequences { get; set; } - - [JsonProperty(Order = 50, PropertyName = "structures")] - public List? Structures { get; set; } - } + [JsonProperty(Order = 40, PropertyName = "sequences")] + public List Sequences { get; set; } + + [JsonProperty(Order = 50, PropertyName = "structures")] + public List? Structures { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/Metadata.cs b/src/IIIF/IIIF/Presentation/V2/Metadata.cs index 3c9b31c..4be28cb 100644 --- a/src/IIIF/IIIF/Presentation/V2/Metadata.cs +++ b/src/IIIF/IIIF/Presentation/V2/Metadata.cs @@ -1,18 +1,17 @@ using IIIF.Presentation.V2.Strings; using Newtonsoft.Json; -namespace IIIF.Presentation.V2 +namespace IIIF.Presentation.V2; + +/// +/// A short, descriptive entry consisting of human readable label and value to be displayed to the user. +/// +/// See https://iiif.io/api/presentation/2.1/#metadata +public class Metadata { - /// - /// A short, descriptive entry consisting of human readable label and value to be displayed to the user. - /// - /// See https://iiif.io/api/presentation/2.1/#metadata - public class Metadata - { - [JsonProperty(Order = 1, PropertyName = "label")] - public MetaDataValue Label { get; set; } + [JsonProperty(Order = 1, PropertyName = "label")] + public MetaDataValue Label { get; set; } - [JsonProperty(Order = 2, PropertyName = "value")] - public MetaDataValue Value { get; set; } - } + [JsonProperty(Order = 2, PropertyName = "value")] + public MetaDataValue Value { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/MetadataX.cs b/src/IIIF/IIIF/Presentation/V2/MetadataX.cs index 46e1bf9..588ccc8 100644 --- a/src/IIIF/IIIF/Presentation/V2/MetadataX.cs +++ b/src/IIIF/IIIF/Presentation/V2/MetadataX.cs @@ -1,25 +1,24 @@ using System.Collections.Generic; using System.Linq; -namespace IIIF.Presentation.V2 +namespace IIIF.Presentation.V2; + +/// +/// A collection of extension methods for working with elements. +/// +public static class MetadataX { /// - /// A collection of extension methods for working with elements. + /// Get value of metadata with specified label. Expected that there is a Single matching element. /// - public static class MetadataX + /// Metadata items to get item from. + /// Metadata label. + /// Value of metadata with label, or null if not found. + public static string? GetValueByLabel(this List? metadata, string label) { - /// - /// Get value of metadata with specified label. Expected that there is a Single matching element. - /// - /// Metadata items to get item from. - /// Metadata label. - /// Value of metadata with label, or null if not found. - public static string? GetValueByLabel(this List? metadata, string label) - { - if (metadata == null || metadata.Count == 0) return null; + if (metadata == null || metadata.Count == 0) return null; - var languageValue = metadata.SingleOrDefault(m => m.Label.LanguageValues.Exists(lv => lv.Value == label)); - return languageValue?.Value.LanguageValues.FirstOrDefault()?.Value; - } + var languageValue = metadata.SingleOrDefault(m => m.Label.LanguageValues.Exists(lv => lv.Value == label)); + return languageValue?.Value.LanguageValues.FirstOrDefault()?.Value; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/Range.cs b/src/IIIF/IIIF/Presentation/V2/Range.cs index 2d4433d..e3865e8 100644 --- a/src/IIIF/IIIF/Presentation/V2/Range.cs +++ b/src/IIIF/IIIF/Presentation/V2/Range.cs @@ -2,28 +2,27 @@ using System.Collections.Generic; using Newtonsoft.Json; -namespace IIIF.Presentation.V2 +namespace IIIF.Presentation.V2; + +public class Range : IIIFPresentationBase { - public class Range : IIIFPresentationBase + public override string? Type { - public override string? Type - { - get => "sc:Range"; - set => throw new NotImplementedException(); - } + get => "sc:Range"; + set => throw new NotImplementedException(); + } - [JsonProperty(Order = 31, PropertyName = "viewingDirection")] - public string? ViewingDirection { get; set; } + [JsonProperty(Order = 31, PropertyName = "viewingDirection")] + public string? ViewingDirection { get; set; } - [JsonProperty(Order = 31, PropertyName = "startCanvas")] - public Uri? StartCanvas { get; set; } - - // URIs of ranges - [JsonProperty(Order = 41, PropertyName = "ranges")] - public List? Ranges { get; set; } - - // URIs of canvases - [JsonProperty(Order = 42, PropertyName = "canvases")] - public List? Canvases { get; set; } - } + [JsonProperty(Order = 31, PropertyName = "startCanvas")] + public Uri? StartCanvas { get; set; } + + // URIs of ranges + [JsonProperty(Order = 41, PropertyName = "ranges")] + public List? Ranges { get; set; } + + // URIs of canvases + [JsonProperty(Order = 42, PropertyName = "canvases")] + public List? Canvases { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/Resource.cs b/src/IIIF/IIIF/Presentation/V2/Resource.cs index ee9277e..2d37d30 100644 --- a/src/IIIF/IIIF/Presentation/V2/Resource.cs +++ b/src/IIIF/IIIF/Presentation/V2/Resource.cs @@ -2,17 +2,16 @@ using IIIF.Serialisation; using Newtonsoft.Json; -namespace IIIF.Presentation.V2 +namespace IIIF.Presentation.V2; + +public class Resource : ResourceBase { - public class Resource : ResourceBase - { - [JsonProperty(Order = 10, PropertyName = "format")] - public string? Format { get; set; } + [JsonProperty(Order = 10, PropertyName = "format")] + public string? Format { get; set; } - [JsonProperty(Order = 99, PropertyName = "service")] - [ObjectIfSingle] - public List? Service { get; set; } + [JsonProperty(Order = 99, PropertyName = "service")] + [ObjectIfSingle] + public List? Service { get; set; } - public override string? Type { get; set; } - } + public override string? Type { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/ResourceBase.cs b/src/IIIF/IIIF/Presentation/V2/ResourceBase.cs index b5ca604..9571fd9 100644 --- a/src/IIIF/IIIF/Presentation/V2/ResourceBase.cs +++ b/src/IIIF/IIIF/Presentation/V2/ResourceBase.cs @@ -1,26 +1,24 @@ using IIIF.Presentation.V2.Strings; using Newtonsoft.Json; -namespace IIIF.Presentation.V2 +namespace IIIF.Presentation.V2; + +/// +/// Common base for all legacy/pre-v3 IIIF models. +/// +public abstract class ResourceBase : JsonLdBase { - /// - /// Common base for all legacy/pre-v3 IIIF models. - /// - public abstract class ResourceBase : JsonLdBase - { - [JsonProperty(PropertyName = "@id", Order = 2)] - public string? Id { get; set; } + [JsonProperty(PropertyName = "@id", Order = 2)] + public string? Id { get; set; } + + [JsonProperty(PropertyName = "@type", Order = 3)] + public abstract string? Type { get; set; } + + [JsonProperty(Order = 4)] public string? Profile { get; set; } + + [JsonProperty(Order = 11, PropertyName = "label")] + public MetaDataValue? Label { get; set; } - [JsonProperty(PropertyName = "@type", Order = 3)] - public abstract string? Type { get; set; } - - [JsonProperty(Order = 4)] - public string? Profile { get; set; } - - [JsonProperty(Order = 11, PropertyName = "label")] - public MetaDataValue? Label { get; set; } - - [JsonProperty(Order = 13, PropertyName = "description")] - public MetaDataValue? Description { get; set; } - } + [JsonProperty(Order = 13, PropertyName = "description")] + public MetaDataValue? Description { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/Sequence.cs b/src/IIIF/IIIF/Presentation/V2/Sequence.cs index 4102b29..c3f62ef 100644 --- a/src/IIIF/IIIF/Presentation/V2/Sequence.cs +++ b/src/IIIF/IIIF/Presentation/V2/Sequence.cs @@ -1,23 +1,22 @@ using System.Collections.Generic; using Newtonsoft.Json; -namespace IIIF.Presentation.V2 +namespace IIIF.Presentation.V2; + +public class Sequence : IIIFPresentationBase { - public class Sequence : IIIFPresentationBase + public override string? Type { - public override string? Type - { - get => "sc:Sequence"; - set => throw new System.NotImplementedException(); - } + get => "sc:Sequence"; + set => throw new System.NotImplementedException(); + } - [JsonProperty(Order = 31, PropertyName = "startCanvas")] - public string StartCanvas { get; set; } + [JsonProperty(Order = 31, PropertyName = "startCanvas")] + public string StartCanvas { get; set; } - [JsonProperty(Order = 31, PropertyName = "viewingDirection")] - public string ViewingDirection { get; set; } + [JsonProperty(Order = 31, PropertyName = "viewingDirection")] + public string ViewingDirection { get; set; } - [JsonProperty(Order = 50, PropertyName = "canvases")] - public List Canvases { get; set; } - } + [JsonProperty(Order = 50, PropertyName = "canvases")] + public List Canvases { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/Serialisation/MetaDataValueSerialiser.cs b/src/IIIF/IIIF/Presentation/V2/Serialisation/MetaDataValueSerialiser.cs index a769728..7a0674f 100644 --- a/src/IIIF/IIIF/Presentation/V2/Serialisation/MetaDataValueSerialiser.cs +++ b/src/IIIF/IIIF/Presentation/V2/Serialisation/MetaDataValueSerialiser.cs @@ -4,101 +4,84 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace IIIF.Presentation.V2.Serialisation +namespace IIIF.Presentation.V2.Serialisation; + +/// +/// JsonConverter for objects. +/// +public class MetaDataValueSerialiser : JsonConverter { - /// - /// JsonConverter for objects. - /// - public class MetaDataValueSerialiser : JsonConverter + public override void WriteJson(JsonWriter writer, MetaDataValue? value, JsonSerializer serializer) { - public override void WriteJson(JsonWriter writer, MetaDataValue? value, JsonSerializer serializer) - { - if (value == null) - { - throw new ArgumentException( - $"MetaDataValueSerialiser cannot serialise a {value.GetType().Name}", nameof(value)); - } + if (value == null) + throw new ArgumentException( + $"MetaDataValueSerialiser cannot serialise a {value.GetType().Name}", nameof(value)); - if (value.LanguageValues.Count == 0) - { - throw new ArgumentException( - $"MetaDataValueSerialiser cannot serialise an empty array {value.GetType().Name}", nameof(value)); - } + if (value.LanguageValues.Count == 0) + throw new ArgumentException( + $"MetaDataValueSerialiser cannot serialise an empty array {value.GetType().Name}", nameof(value)); - if (value.LanguageValues.Count > 1) - { - writer.WriteStartArray(); - } + if (value.LanguageValues.Count > 1) writer.WriteStartArray(); - foreach (var lv in value.LanguageValues) + foreach (var lv in value.LanguageValues) + if (string.IsNullOrWhiteSpace(lv.Language)) { - if (string.IsNullOrWhiteSpace(lv.Language)) - { - writer.WriteValue(lv.Value); - } - else - { - writer.WriteStartObject(); - writer.WritePropertyName("@value"); - writer.WriteValue(lv.Value); - writer.WritePropertyName("@language"); - writer.WriteValue(lv.Language); - writer.WriteEndObject(); - } + writer.WriteValue(lv.Value); } - - if (value.LanguageValues.Count > 1) + else { - writer.WriteEndArray(); + writer.WriteStartObject(); + writer.WritePropertyName("@value"); + writer.WriteValue(lv.Value); + writer.WritePropertyName("@language"); + writer.WriteValue(lv.Language); + writer.WriteEndObject(); } - } - - public override MetaDataValue? ReadJson(JsonReader reader, Type objectType, MetaDataValue? existingValue, bool hasExistingValue, - JsonSerializer serializer) - { - if (reader.TokenType == JsonToken.Null) return null; - if (reader.TokenType == JsonToken.String) - { - // Basic string, single value only - return new MetaDataValue(reader.Value.ToString()); - } + if (value.LanguageValues.Count > 1) writer.WriteEndArray(); + } - if (reader.TokenType == JsonToken.StartObject) - { - // Single language value - var jo = JObject.Load(reader); - var languageValue = jo.ToObject(); - return new MetaDataValue(new List { languageValue }); - } + public override MetaDataValue? ReadJson(JsonReader reader, Type objectType, MetaDataValue? existingValue, + bool hasExistingValue, + JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; - if (reader.TokenType == JsonToken.StartArray) - { - var jo = JArray.Load(reader); - if (jo.First.Type == JTokenType.String) - { - var values = jo.ToObject(); - return ConvertFromStringArray(values); - } - else - { - var languageValues = jo.ToObject(); - return new MetaDataValue(languageValues); - } - } + if (reader.TokenType == JsonToken.String) + // Basic string, single value only + return new MetaDataValue(reader.Value.ToString()); - throw new FormatException("Unable to convert provided object to MetaDataValue"); + if (reader.TokenType == JsonToken.StartObject) + { + // Single language value + var jo = JObject.Load(reader); + var languageValue = jo.ToObject(); + return new MetaDataValue(new List { languageValue }); } - private MetaDataValue ConvertFromStringArray(IReadOnlyList values) + if (reader.TokenType == JsonToken.StartArray) { - MetaDataValue mdv = new MetaDataValue(values[0]); - for (int x = 1; x < values.Count; x++) + var jo = JArray.Load(reader); + if (jo.First.Type == JTokenType.String) { - mdv.LanguageValues.Add(new LanguageValue { Value = values[x] }); + var values = jo.ToObject(); + return ConvertFromStringArray(values); + } + else + { + var languageValues = jo.ToObject(); + return new MetaDataValue(languageValues); } - - return mdv; } + + throw new FormatException("Unable to convert provided object to MetaDataValue"); + } + + private MetaDataValue ConvertFromStringArray(IReadOnlyList values) + { + var mdv = new MetaDataValue(values[0]); + for (var x = 1; x < values.Count; x++) mdv.LanguageValues.Add(new LanguageValue { Value = values[x] }); + + return mdv; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/Strings/LanguageValue.cs b/src/IIIF/IIIF/Presentation/V2/Strings/LanguageValue.cs index cd0772c..01922bf 100644 --- a/src/IIIF/IIIF/Presentation/V2/Strings/LanguageValue.cs +++ b/src/IIIF/IIIF/Presentation/V2/Strings/LanguageValue.cs @@ -1,14 +1,12 @@ using Newtonsoft.Json; -namespace IIIF.Presentation.V2.Strings -{ - /// - /// Represents a value object with optional language. - /// - public class LanguageValue : ValueObject - { - [JsonProperty(Order = 4, PropertyName = "@language")] - public string? Language { get; set; } +namespace IIIF.Presentation.V2.Strings; - } +/// +/// Represents a value object with optional language. +/// +public class LanguageValue : ValueObject +{ + [JsonProperty(Order = 4, PropertyName = "@language")] + public string? Language { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/Strings/MetaDataValue.cs b/src/IIIF/IIIF/Presentation/V2/Strings/MetaDataValue.cs index a64fdca..e64387e 100644 --- a/src/IIIF/IIIF/Presentation/V2/Strings/MetaDataValue.cs +++ b/src/IIIF/IIIF/Presentation/V2/Strings/MetaDataValue.cs @@ -5,56 +5,61 @@ using IIIF.Presentation.V3.Strings; using Newtonsoft.Json; -namespace IIIF.Presentation.V2.Strings +namespace IIIF.Presentation.V2.Strings; + +/// +/// A collection of for metadata. +/// +[JsonConverter(typeof(MetaDataValueSerialiser))] +public class MetaDataValue { + public List LanguageValues { get; set; } + + public MetaDataValue(string value) + { + LanguageValues = new List { new() { Value = value } }; + } + + public MetaDataValue(string value, string language) + { + LanguageValues = new List { new() { Value = value, Language = language } }; + } + + public MetaDataValue(IEnumerable languageValues) + { + LanguageValues = languageValues.ToList(); + } + /// - /// A collection of for metadata. + /// Create a new MetaDataValue object from specified LanguageMap. /// - [JsonConverter(typeof(MetaDataValueSerialiser))] - public class MetaDataValue + /// LanguageMap to convert to MetaDataValue + /// If true, language not set + /// Optional predicate to filter MetaDataValues. + /// Null if LanguageMap null, else new MetaDataValue object + public static MetaDataValue? Create(LanguageMap? languageMap, bool ignoreLanguage = false, + Func? languagePredicate = null) { - public List LanguageValues { get; set; } - - public MetaDataValue(string value) - => LanguageValues = new List {new() {Value = value}}; - - public MetaDataValue(string value, string language) - => LanguageValues = new List {new() {Value = value, Language = language}}; - - public MetaDataValue(IEnumerable languageValues) - => LanguageValues = languageValues.ToList(); - - /// - /// Create a new MetaDataValue object from specified LanguageMap. - /// - /// LanguageMap to convert to MetaDataValue - /// If true, language not set - /// Optional predicate to filter MetaDataValues. - /// Null if LanguageMap null, else new MetaDataValue object - public static MetaDataValue? Create(LanguageMap? languageMap, bool ignoreLanguage = false, - Func? languagePredicate = null) + // "none" is used in P3 if language unknown + const string unknownLanguage = "none"; + if (languageMap == null) return null; + + languagePredicate ??= s => true; + + var langVals = new List(); + foreach (var kvp in languageMap) { - // "none" is used in P3 if language unknown - const string unknownLanguage = "none"; - if (languageMap == null) return null; - - languagePredicate ??= s => true; - - var langVals = new List(); - foreach (var kvp in languageMap) - { - var language = ignoreLanguage || kvp.Key == unknownLanguage ? null : kvp.Key; - - langVals.AddRange(kvp.Value - .Where(languagePredicate) - .Select(values => new LanguageValue - { - Language = language, - Value = values - })); - } - - return new MetaDataValue(langVals); + var language = ignoreLanguage || kvp.Key == unknownLanguage ? null : kvp.Key; + + langVals.AddRange(kvp.Value + .Where(languagePredicate) + .Select(values => new LanguageValue + { + Language = language, + Value = values + })); } + + return new MetaDataValue(langVals); } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/Thumbnail.cs b/src/IIIF/IIIF/Presentation/V2/Thumbnail.cs index 93c7e84..915b280 100644 --- a/src/IIIF/IIIF/Presentation/V2/Thumbnail.cs +++ b/src/IIIF/IIIF/Presentation/V2/Thumbnail.cs @@ -2,14 +2,13 @@ using IIIF.Serialisation; using Newtonsoft.Json; -namespace IIIF.Presentation.V2 +namespace IIIF.Presentation.V2; + +public class Thumbnail : ResourceBase { - public class Thumbnail : ResourceBase - { - public override string? Type { get; set; } = "dctypes:Image"; + public override string? Type { get; set; } = "dctypes:Image"; - [JsonProperty(Order = 28)] - [ObjectIfSingle] - public List? Service { get; set; } - } + [JsonProperty(Order = 28)] + [ObjectIfSingle] + public List? Service { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V2/ValueObject.cs b/src/IIIF/IIIF/Presentation/V2/ValueObject.cs index bc17790..1474884 100644 --- a/src/IIIF/IIIF/Presentation/V2/ValueObject.cs +++ b/src/IIIF/IIIF/Presentation/V2/ValueObject.cs @@ -1,17 +1,15 @@ using Newtonsoft.Json; -namespace IIIF.Presentation.V2 -{ - public abstract class ValueObject - { - [JsonProperty(Order = 1, PropertyName = "@context")] - public string Context { get; set; } +namespace IIIF.Presentation.V2; - [JsonProperty(Order = 2, PropertyName = "@index")] - public string Index { get; set; } +public abstract class ValueObject +{ + [JsonProperty(Order = 1, PropertyName = "@context")] + public string Context { get; set; } - [JsonProperty(Order = 3, PropertyName = "@value")] - public string Value { get; set; } + [JsonProperty(Order = 2, PropertyName = "@index")] + public string Index { get; set; } - } + [JsonProperty(Order = 3, PropertyName = "@value")] + public string Value { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Agent.cs b/src/IIIF/IIIF/Presentation/V3/Agent.cs index b1ed186..a003b4f 100644 --- a/src/IIIF/IIIF/Presentation/V3/Agent.cs +++ b/src/IIIF/IIIF/Presentation/V3/Agent.cs @@ -2,13 +2,11 @@ using IIIF.Presentation.V3.Content; using Newtonsoft.Json; -namespace IIIF.Presentation.V3 +namespace IIIF.Presentation.V3; + +public class Agent : ResourceBase { - public class Agent : ResourceBase - { - public override string Type => nameof(Agent); - - [JsonProperty(Order = 500)] - public List? Logo { get; set; } - } -} + public override string Type => nameof(Agent); + + [JsonProperty(Order = 500)] public List? Logo { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Annotation/Annotation.cs b/src/IIIF/IIIF/Presentation/V3/Annotation/Annotation.cs index 2afcedf..fd0381c 100644 --- a/src/IIIF/IIIF/Presentation/V3/Annotation/Annotation.cs +++ b/src/IIIF/IIIF/Presentation/V3/Annotation/Annotation.cs @@ -1,27 +1,25 @@ using IIIF.Serialisation; using Newtonsoft.Json; -namespace IIIF.Presentation.V3.Annotation +namespace IIIF.Presentation.V3.Annotation; + +public class Annotation : ResourceBase, IAnnotation { - public class Annotation : ResourceBase, IAnnotation - { - public override string Type => nameof(Annotation); - - [JsonProperty(Order = 10)] - public virtual string? Motivation { get; set; } - - /// - /// A mode associated with an Annotation that is to be applied to the rendering of any time-based media. - /// see timemode - /// - [JsonProperty(Order = 21)] - public string? TimeMode { get; set; } - - /// - /// Note that this is a IIIF-specific use of target; can be anything in W3C - /// - [JsonProperty(Order = 900)] - [JsonConverter(typeof(TargetConverter))] - public IStructuralLocation? Target { get; set; } - } -} + public override string Type => nameof(Annotation); + + [JsonProperty(Order = 10)] public virtual string? Motivation { get; set; } + + /// + /// A mode associated with an Annotation that is to be applied to the rendering of any time-based media. + /// see timemode + /// + [JsonProperty(Order = 21)] + public string? TimeMode { get; set; } + + /// + /// Note that this is a IIIF-specific use of target; can be anything in W3C + /// + [JsonProperty(Order = 900)] + [JsonConverter(typeof(TargetConverter))] + public IStructuralLocation? Target { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Annotation/AnnotationCollection.cs b/src/IIIF/IIIF/Presentation/V3/Annotation/AnnotationCollection.cs index d7f0f55..c9efb1c 100644 --- a/src/IIIF/IIIF/Presentation/V3/Annotation/AnnotationCollection.cs +++ b/src/IIIF/IIIF/Presentation/V3/Annotation/AnnotationCollection.cs @@ -1,7 +1,6 @@ -namespace IIIF.Presentation.V3.Annotation +namespace IIIF.Presentation.V3.Annotation; + +public class AnnotationCollection : ResourceBase { - public class AnnotationCollection : ResourceBase - { - public override string Type => nameof(AnnotationCollection); - } -} + public override string Type => nameof(AnnotationCollection); +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Annotation/AnnotationPage.cs b/src/IIIF/IIIF/Presentation/V3/Annotation/AnnotationPage.cs index c34081f..acca9c9 100644 --- a/src/IIIF/IIIF/Presentation/V3/Annotation/AnnotationPage.cs +++ b/src/IIIF/IIIF/Presentation/V3/Annotation/AnnotationPage.cs @@ -1,13 +1,11 @@ using System.Collections.Generic; using Newtonsoft.Json; -namespace IIIF.Presentation.V3.Annotation +namespace IIIF.Presentation.V3.Annotation; + +public class AnnotationPage : ResourceBase { - public class AnnotationPage : ResourceBase - { - public override string Type => nameof(AnnotationPage); - - [JsonProperty(Order = 300)] - public List? Items { get; set; } - } -} + public override string Type => nameof(AnnotationPage); + + [JsonProperty(Order = 300)] public List? Items { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Annotation/ClassifyingBody.cs b/src/IIIF/IIIF/Presentation/V3/Annotation/ClassifyingBody.cs index 534fd8a..c19231a 100644 --- a/src/IIIF/IIIF/Presentation/V3/Annotation/ClassifyingBody.cs +++ b/src/IIIF/IIIF/Presentation/V3/Annotation/ClassifyingBody.cs @@ -1,12 +1,11 @@ -namespace IIIF.Presentation.V3.Annotation +namespace IIIF.Presentation.V3.Annotation; + +public class ClassifyingBody : ResourceBase { - public class ClassifyingBody : ResourceBase + public ClassifyingBody(string classifyingType) { - public ClassifyingBody(string classifyingType) - { - Id = classifyingType; - } - - public override string Type => null; + Id = classifyingType; } + + public override string Type => null; } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Annotation/IAnnotation.cs b/src/IIIF/IIIF/Presentation/V3/Annotation/IAnnotation.cs index 19e1be2..52ad76a 100644 --- a/src/IIIF/IIIF/Presentation/V3/Annotation/IAnnotation.cs +++ b/src/IIIF/IIIF/Presentation/V3/Annotation/IAnnotation.cs @@ -1,6 +1,5 @@ -namespace IIIF.Presentation.V3.Annotation +namespace IIIF.Presentation.V3.Annotation; + +public interface IAnnotation { - public interface IAnnotation - { - } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Annotation/IPaintable.cs b/src/IIIF/IIIF/Presentation/V3/Annotation/IPaintable.cs index cc6c42f..f6ee0a6 100644 --- a/src/IIIF/IIIF/Presentation/V3/Annotation/IPaintable.cs +++ b/src/IIIF/IIIF/Presentation/V3/Annotation/IPaintable.cs @@ -1,14 +1,12 @@ - -using System.Collections.Generic; +using System.Collections.Generic; -namespace IIIF.Presentation.V3.Annotation +namespace IIIF.Presentation.V3.Annotation; + +/// +/// Marker interface for things that can be painted ONTO a Canvas. +/// This includes other canvases. +/// +public interface IPaintable { - /// - /// Marker interface for things that can be painted ONTO a Canvas. - /// This includes other canvases. - /// - public interface IPaintable - { - List? Service { get; set; } - } -} + List? Service { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Annotation/PaintingAnnotation.cs b/src/IIIF/IIIF/Presentation/V3/Annotation/PaintingAnnotation.cs index ad9b81a..95b2a61 100644 --- a/src/IIIF/IIIF/Presentation/V3/Annotation/PaintingAnnotation.cs +++ b/src/IIIF/IIIF/Presentation/V3/Annotation/PaintingAnnotation.cs @@ -1,12 +1,10 @@ using Newtonsoft.Json; -namespace IIIF.Presentation.V3.Annotation +namespace IIIF.Presentation.V3.Annotation; + +public class PaintingAnnotation : Annotation { - public class PaintingAnnotation : Annotation - { - public override string Motivation => Constants.Motivation.Painting; - - [JsonProperty(Order = 500)] - public IPaintable? Body { get; set; } - } -} + public override string Motivation => Constants.Motivation.Painting; + + [JsonProperty(Order = 500)] public IPaintable? Body { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Annotation/PaintingChoice.cs b/src/IIIF/IIIF/Presentation/V3/Annotation/PaintingChoice.cs index a01c69a..b34a1de 100644 --- a/src/IIIF/IIIF/Presentation/V3/Annotation/PaintingChoice.cs +++ b/src/IIIF/IIIF/Presentation/V3/Annotation/PaintingChoice.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; -namespace IIIF.Presentation.V3.Annotation +namespace IIIF.Presentation.V3.Annotation; + +public class PaintingChoice : IPaintable { - public class PaintingChoice : IPaintable - { - public string Type => "Choice"; - public List? Items { get; set; } - - public List? Service { get; set; } - } + public string Type => "Choice"; + public List? Items { get; set; } + + public List? Service { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Annotation/SupplementingAnnotation.cs b/src/IIIF/IIIF/Presentation/V3/Annotation/SupplementingAnnotation.cs index 29f79b1..8cd273c 100644 --- a/src/IIIF/IIIF/Presentation/V3/Annotation/SupplementingAnnotation.cs +++ b/src/IIIF/IIIF/Presentation/V3/Annotation/SupplementingAnnotation.cs @@ -1,9 +1,8 @@ -namespace IIIF.Presentation.V3.Annotation +namespace IIIF.Presentation.V3.Annotation; + +public class SupplementingDocumentAnnotation : Annotation { - public class SupplementingDocumentAnnotation : Annotation - { - public override string Motivation => Constants.Motivation.Supplementing; - - public ResourceBase? Body { get; set; } - } + public override string Motivation => Constants.Motivation.Supplementing; + + public ResourceBase? Body { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Annotation/TextualBody.cs b/src/IIIF/IIIF/Presentation/V3/Annotation/TextualBody.cs index 1c300ca..7654bac 100644 --- a/src/IIIF/IIIF/Presentation/V3/Annotation/TextualBody.cs +++ b/src/IIIF/IIIF/Presentation/V3/Annotation/TextualBody.cs @@ -1,21 +1,20 @@ -namespace IIIF.Presentation.V3.Annotation +namespace IIIF.Presentation.V3.Annotation; + +public class TextualBody : ResourceBase { - public class TextualBody : ResourceBase + public TextualBody(string value) { - public TextualBody(string value) - { - Value = value; - } - - public TextualBody(string value, string format) - { - Value = value; - Format = format; - } - - public override string Type => nameof(TextualBody); - - public string? Value { get; set; } - public string? Format { get; set; } + Value = value; } + + public TextualBody(string value, string format) + { + Value = value; + Format = format; + } + + public override string Type => nameof(TextualBody); + + public string? Value { get; set; } + public string? Format { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Annotation/TypeClassifyingAnnotation.cs b/src/IIIF/IIIF/Presentation/V3/Annotation/TypeClassifyingAnnotation.cs index c31dd58..3ffb2a3 100644 --- a/src/IIIF/IIIF/Presentation/V3/Annotation/TypeClassifyingAnnotation.cs +++ b/src/IIIF/IIIF/Presentation/V3/Annotation/TypeClassifyingAnnotation.cs @@ -1,20 +1,19 @@ -namespace IIIF.Presentation.V3.Annotation +namespace IIIF.Presentation.V3.Annotation; + +public class TypeClassifyingAnnotation : Annotation { - public class TypeClassifyingAnnotation : Annotation - { - public override string Motivation => Constants.Motivation.Classifying; - - public ResourceBase? Body { get; set; } - } + public override string Motivation => Constants.Motivation.Classifying; - /// - /// Marker class for deserialising json object with an unknown motivation - /// - internal sealed class UnknownMotivation : Annotation + public ResourceBase? Body { get; set; } +} + +/// +/// Marker class for deserialising json object with an unknown motivation +/// +internal sealed class UnknownMotivation : Annotation +{ + public UnknownMotivation(string motivation) { - public UnknownMotivation(string motivation) - { - Motivation = motivation; - } + Motivation = motivation; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Canvas.cs b/src/IIIF/IIIF/Presentation/V3/Canvas.cs index 653fd29..5f979a9 100644 --- a/src/IIIF/IIIF/Presentation/V3/Canvas.cs +++ b/src/IIIF/IIIF/Presentation/V3/Canvas.cs @@ -2,43 +2,42 @@ using IIIF.Presentation.V3.Annotation; using Newtonsoft.Json; -namespace IIIF.Presentation.V3 +namespace IIIF.Presentation.V3; + +/// +/// A Canvas represents an individual page or view and acts as a central point for assembling the different content +/// resources that make up the display. +/// See Canvas docs. +/// +public class + Canvas : StructureBase, IStructuralLocation, IPaintable // but not ISpatial or ITemporal - that's for content { + public override string Type => nameof(Canvas); + + /// + /// The Width of the Canvas. This value does not have a unit. + /// + [JsonProperty(Order = 11)] + public int? Width { get; set; } + /// - /// A Canvas represents an individual page or view and acts as a central point for assembling the different content - /// resources that make up the display. - /// See Canvas docs. + /// The Height of the Canvas. This value does not have a unit. /// - public class Canvas : StructureBase, IStructuralLocation, IPaintable // but not ISpatial or ITemporal - that's for content - { - public override string Type => nameof(Canvas); + [JsonProperty(Order = 12)] + public int? Height { get; set; } - /// - /// The Width of the Canvas. This value does not have a unit. - /// - [JsonProperty(Order = 11)] - public int? Width { get; set; } - - /// - /// The Height of the Canvas. This value does not have a unit. - /// - [JsonProperty(Order = 12)] - public int? Height { get; set; } - - /// - /// The Duration of the Canvas, in seconds. - /// - [JsonProperty(Order = 13)] - public double? Duration { get; set; } - - [JsonProperty(Order = 300)] - public List? Items { get; set; } + /// + /// The Duration of the Canvas, in seconds. + /// + [JsonProperty(Order = 13)] + public double? Duration { get; set; } + + [JsonProperty(Order = 300)] public List? Items { get; set; } - /// - /// Used to control serialisation logic for Canvas items that are Targets. - /// If true then Target is serialised as simple Id string only. - /// - [JsonIgnore] - public bool SerialiseTargetAsId { get; set; } - } -} + /// + /// Used to control serialisation logic for Canvas items that are Targets. + /// If true then Target is serialised as simple Id string only. + /// + [JsonIgnore] + public bool SerialiseTargetAsId { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Collection.cs b/src/IIIF/IIIF/Presentation/V3/Collection.cs index ee7c428..d0347fb 100644 --- a/src/IIIF/IIIF/Presentation/V3/Collection.cs +++ b/src/IIIF/IIIF/Presentation/V3/Collection.cs @@ -1,34 +1,33 @@ using System.Collections.Generic; using Newtonsoft.Json; -namespace IIIF.Presentation.V3 +namespace IIIF.Presentation.V3; + +/// +/// Collections are used to list the s available for viewing. +/// May include both other Collections and Manifests to form a tree-structured hierarchy. +/// See Collection docs. +/// +public class Collection : StructureBase, ICollectionItem { + public override string Type => nameof(Collection); + + /// + /// The direction in which a set of Canvases should be displayed to the user + /// See viewingdirection + /// + [JsonProperty(Order = 32)] + public string? ViewingDirection { get; set; } + + /// + /// Note that this is not the same as ResourceBase::Service + /// + [JsonProperty(Order = 102)] + public List? Services { get; set; } + /// - /// Collections are used to list the s available for viewing. - /// May include both other Collections and Manifests to form a tree-structured hierarchy. - /// See Collection docs. + /// Embedded Collections or Referenced Manifests/Collections. /// - public class Collection : StructureBase, ICollectionItem - { - public override string Type => nameof(Collection); - - /// - /// The direction in which a set of Canvases should be displayed to the user - /// See viewingdirection - /// - [JsonProperty(Order = 32)] - public string? ViewingDirection { get; set; } - - /// - /// Note that this is not the same as ResourceBase::Service - /// - [JsonProperty(Order = 102)] - public List? Services { get; set; } - - /// - /// Embedded Collections or Referenced Manifests/Collections. - /// - [JsonProperty(Order = 300)] - public List? Items { get; set; } - } -} + [JsonProperty(Order = 300)] + public List? Items { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Constants/Behavior.cs b/src/IIIF/IIIF/Presentation/V3/Constants/Behavior.cs index 4a89d8e..4999d94 100644 --- a/src/IIIF/IIIF/Presentation/V3/Constants/Behavior.cs +++ b/src/IIIF/IIIF/Presentation/V3/Constants/Behavior.cs @@ -1,31 +1,30 @@ -namespace IIIF.Presentation.V3.Constants +namespace IIIF.Presentation.V3.Constants; + +public static class Behavior { - public static class Behavior - { - // Temporal Behaviors - public const string AutoAdvance = "auto-advance"; - public const string NoAutoAdvance = "no-auto-advance"; - public const string Repeat = "repeat"; - public const string NoRepeat = "no-repeat"; + // Temporal Behaviors + public const string AutoAdvance = "auto-advance"; + public const string NoAutoAdvance = "no-auto-advance"; + public const string Repeat = "repeat"; + public const string NoRepeat = "no-repeat"; - // Layout Behaviors - public const string Unordered = "unordered"; - public const string Individuals = "individuals"; - public const string Continuous = "continuous"; - public const string Paged = "paged"; - public const string FacingPages = "facing-pages"; - public const string NonPaged = "non-paged"; + // Layout Behaviors + public const string Unordered = "unordered"; + public const string Individuals = "individuals"; + public const string Continuous = "continuous"; + public const string Paged = "paged"; + public const string FacingPages = "facing-pages"; + public const string NonPaged = "non-paged"; - // Collection Behaviors - public const string MultiPart = "multi-part"; - public const string Together = "together"; + // Collection Behaviors + public const string MultiPart = "multi-part"; + public const string Together = "together"; - // Range Behaviors - public const string Sequence = "sequence"; - public const string ThumbnailNav = "thumbnail-nav"; - public const string NoNav = "no-nav"; + // Range Behaviors + public const string Sequence = "sequence"; + public const string ThumbnailNav = "thumbnail-nav"; + public const string NoNav = "no-nav"; - // Miscellaneous Behaviors - public const string Hidden = "hidden"; - } -} + // Miscellaneous Behaviors + public const string Hidden = "hidden"; +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Constants/Motivation.cs b/src/IIIF/IIIF/Presentation/V3/Constants/Motivation.cs index 95c4bc0..3d595b9 100644 --- a/src/IIIF/IIIF/Presentation/V3/Constants/Motivation.cs +++ b/src/IIIF/IIIF/Presentation/V3/Constants/Motivation.cs @@ -1,100 +1,98 @@ - -namespace IIIF.Presentation.V3.Constants +namespace IIIF.Presentation.V3.Constants; + +public static class Motivation { - public static class Motivation - { - // IIIF-specific extension motivations - - /// - /// The content can be thought of as being *of* the Canvas. - /// For example, an image of a book page, that is expected to be rendered to a user. - /// - public const string Painting = "painting"; - - /// - /// The content can be thought of as being *from* the Canvas. - /// For example, a textual transcription of a line or a page. - /// - public const string Supplementing = "supplementing"; - - // W3C Annotation motivations - - /// - /// The motivation for when the user intends to assess the target resource in some way, rather than - /// simply make a comment about it. For example to write a review or assessment of a book, assess the - /// quality of a dataset, or provide an assessment of a student's work. - /// - public const string Assessing = "assessing"; - - /// - /// The motivation for when the user intends to create a bookmark to the Target or part thereof. - /// For example an Annotation that bookmarks the point in a text where the reader finished reading. - /// - public const string Bookmarking = "bookmarking"; - - /// - /// The motivation for when the user intends to classify the Target as something. - /// For example to classify an image as a portrait. - /// - public const string Classifying = "classifying"; - - /// - /// The motivation for when the user intends to comment about the Target. - /// For example to provide a commentary about a particular PDF document. - /// - public const string Commenting = "commenting"; - - /// - /// The motivation for when the user intends to describe the Target, - /// as opposed to (for example) a comment about it. - /// For example describing the above PDF's contents, rather than commenting on their accuracy. - /// - public const string Describing = "describing"; - - /// - /// The motivation for when the user intends to request a change or edit to the Target resource. - /// For example an Annotation that requests a typo to be corrected. - /// - public const string Editing = "editing"; - - /// - /// The motivation for when the user intends to highlight the Target resource or segment of it. - /// For example to draw attention to the selected text that the annotator disagrees with. - /// - public const string Highlighting = "highlighting"; - - /// - /// The motivation for when the user intends to assign an identity to the Target. - /// For example to associate the IRI that identifies a city with a mention of the city in a web page. - /// - public const string Identifying = "identifying"; - - /// - /// The motivation for when the user intends to link to a resource related to the Target. - /// - public const string Linking = "linking"; - - /// - /// The motivation for when the user intends to assign some value or quality to the Target. - /// For example annotating an Annotation to moderate it up in a trust network or threaded discussion. - /// - public const string Moderating = "moderating"; - - /// - /// The motivation for when the user intends to ask a question about the Target. - /// For example to ask for assistance with a particular section of text, or question its veracity. - /// - public const string Questioning = "questioning"; - - /// - /// The motivation for when the user intends to reply to a previous statement, - /// either an Annotation or another resource. For example providing the assistance requested in the above. - /// - public const string Replying = "replying"; - - /// - /// The motivation for when the user intends to associate a tag with the Target. - /// - public const string Tagging = "tagging"; - } -} + // IIIF-specific extension motivations + + /// + /// The content can be thought of as being *of* the Canvas. + /// For example, an image of a book page, that is expected to be rendered to a user. + /// + public const string Painting = "painting"; + + /// + /// The content can be thought of as being *from* the Canvas. + /// For example, a textual transcription of a line or a page. + /// + public const string Supplementing = "supplementing"; + + // W3C Annotation motivations + + /// + /// The motivation for when the user intends to assess the target resource in some way, rather than + /// simply make a comment about it. For example to write a review or assessment of a book, assess the + /// quality of a dataset, or provide an assessment of a student's work. + /// + public const string Assessing = "assessing"; + + /// + /// The motivation for when the user intends to create a bookmark to the Target or part thereof. + /// For example an Annotation that bookmarks the point in a text where the reader finished reading. + /// + public const string Bookmarking = "bookmarking"; + + /// + /// The motivation for when the user intends to classify the Target as something. + /// For example to classify an image as a portrait. + /// + public const string Classifying = "classifying"; + + /// + /// The motivation for when the user intends to comment about the Target. + /// For example to provide a commentary about a particular PDF document. + /// + public const string Commenting = "commenting"; + + /// + /// The motivation for when the user intends to describe the Target, + /// as opposed to (for example) a comment about it. + /// For example describing the above PDF's contents, rather than commenting on their accuracy. + /// + public const string Describing = "describing"; + + /// + /// The motivation for when the user intends to request a change or edit to the Target resource. + /// For example an Annotation that requests a typo to be corrected. + /// + public const string Editing = "editing"; + + /// + /// The motivation for when the user intends to highlight the Target resource or segment of it. + /// For example to draw attention to the selected text that the annotator disagrees with. + /// + public const string Highlighting = "highlighting"; + + /// + /// The motivation for when the user intends to assign an identity to the Target. + /// For example to associate the IRI that identifies a city with a mention of the city in a web page. + /// + public const string Identifying = "identifying"; + + /// + /// The motivation for when the user intends to link to a resource related to the Target. + /// + public const string Linking = "linking"; + + /// + /// The motivation for when the user intends to assign some value or quality to the Target. + /// For example annotating an Annotation to moderate it up in a trust network or threaded discussion. + /// + public const string Moderating = "moderating"; + + /// + /// The motivation for when the user intends to ask a question about the Target. + /// For example to ask for assistance with a particular section of text, or question its veracity. + /// + public const string Questioning = "questioning"; + + /// + /// The motivation for when the user intends to reply to a previous statement, + /// either an Annotation or another resource. For example providing the assistance requested in the above. + /// + public const string Replying = "replying"; + + /// + /// The motivation for when the user intends to associate a tag with the Target. + /// + public const string Tagging = "tagging"; +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Constants/Services.cs b/src/IIIF/IIIF/Presentation/V3/Constants/Services.cs index fc079d1..bed9dec 100644 --- a/src/IIIF/IIIF/Presentation/V3/Constants/Services.cs +++ b/src/IIIF/IIIF/Presentation/V3/Constants/Services.cs @@ -1,19 +1,18 @@ -namespace IIIF.Presentation.V3.Constants +namespace IIIF.Presentation.V3.Constants; + +public static class Services { - public static class Services - { - // Defined by Presentation API: - public const string ImageService1 = nameof(ImageService1); - public const string ImageService2 = nameof(ImageService2); + // Defined by Presentation API: + public const string ImageService1 = nameof(ImageService1); + public const string ImageService2 = nameof(ImageService2); - public const string SearchService1 = nameof(SearchService1); - public const string AutoCompleteService1 = nameof(AutoCompleteService1); + public const string SearchService1 = nameof(SearchService1); + public const string AutoCompleteService1 = nameof(AutoCompleteService1); - public const string AuthCookieService1 = nameof(AuthCookieService1); - public const string AuthTokenService1 = nameof(AuthTokenService1); - public const string AuthLogoutService1 = nameof(AuthLogoutService1); + public const string AuthCookieService1 = nameof(AuthCookieService1); + public const string AuthTokenService1 = nameof(AuthTokenService1); + public const string AuthLogoutService1 = nameof(AuthLogoutService1); - // Defined by other IIIF Specifications: - public const string ImageService3 = nameof(ImageService3); - } -} + // Defined by other IIIF Specifications: + public const string ImageService3 = nameof(ImageService3); +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Constants/TimeMode.cs b/src/IIIF/IIIF/Presentation/V3/Constants/TimeMode.cs index 4d32223..7a2025e 100644 --- a/src/IIIF/IIIF/Presentation/V3/Constants/TimeMode.cs +++ b/src/IIIF/IIIF/Presentation/V3/Constants/TimeMode.cs @@ -1,10 +1,8 @@ - -namespace IIIF.Presentation.V3.Constants +namespace IIIF.Presentation.V3.Constants; + +public static class TimeMode { - public static class TimeMode - { - public const string Trim = "trim"; - public const string Scale = "scale"; - public const string Loop = "loop"; - } -} + public const string Trim = "trim"; + public const string Scale = "scale"; + public const string Loop = "loop"; +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Constants/ViewingDirection.cs b/src/IIIF/IIIF/Presentation/V3/Constants/ViewingDirection.cs index 6789eef..2ea24a1 100644 --- a/src/IIIF/IIIF/Presentation/V3/Constants/ViewingDirection.cs +++ b/src/IIIF/IIIF/Presentation/V3/Constants/ViewingDirection.cs @@ -1,28 +1,27 @@ -namespace IIIF.Presentation.V3.Constants +namespace IIIF.Presentation.V3.Constants; + +/// +/// Constants representing the direction in which a set of Canvases should be displayed to the user. +/// +public static class ViewingDirection { /// - /// Constants representing the direction in which a set of Canvases should be displayed to the user. + /// The object is displayed from left to right. The default if not specified. /// - public static class ViewingDirection - { - /// - /// The object is displayed from left to right. The default if not specified. - /// - public const string LeftToRight = "left-to-right"; - - /// - /// The object is displayed from right to left. - /// - public const string RightToLeft = "right-to-left"; - - /// - /// The object is displayed from the top to the bottom. - /// - public const string TopToBottom = "top-to-bottom"; - - /// - /// The object is displayed from the bottom to the top. - /// - public const string BottomToTop = "bottom-to-top"; - } -} + public const string LeftToRight = "left-to-right"; + + /// + /// The object is displayed from right to left. + /// + public const string RightToLeft = "right-to-left"; + + /// + /// The object is displayed from the top to the bottom. + /// + public const string TopToBottom = "top-to-bottom"; + + /// + /// The object is displayed from the bottom to the top. + /// + public const string BottomToTop = "bottom-to-top"; +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Content/Audio.cs b/src/IIIF/IIIF/Presentation/V3/Content/Audio.cs index 295d202..a9f4c53 100644 --- a/src/IIIF/IIIF/Presentation/V3/Content/Audio.cs +++ b/src/IIIF/IIIF/Presentation/V3/Content/Audio.cs @@ -1,11 +1,12 @@ using IIIF.Presentation.V3.Annotation; -namespace IIIF.Presentation.V3.Content +namespace IIIF.Presentation.V3.Content; + +public class Audio : ExternalResource, ITemporal, IPaintable { - public class Audio : ExternalResource, ITemporal, IPaintable - { - public double? Duration { get; set; } + public double? Duration { get; set; } - public Audio() : base("Sound") { } + public Audio() : base("Sound") + { } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Content/ExternalResource.cs b/src/IIIF/IIIF/Presentation/V3/Content/ExternalResource.cs index 9cff363..369dcf8 100644 --- a/src/IIIF/IIIF/Presentation/V3/Content/ExternalResource.cs +++ b/src/IIIF/IIIF/Presentation/V3/Content/ExternalResource.cs @@ -2,33 +2,31 @@ using IIIF.Presentation.V3.Annotation; using Newtonsoft.Json; -namespace IIIF.Presentation.V3.Content +namespace IIIF.Presentation.V3.Content; + +public class ExternalResource : ResourceBase { - public class ExternalResource : ResourceBase + public ExternalResource(string type) { - public ExternalResource(string type) - { - Type = type; - } + Type = type; + } - public override string Type { get; } + public override string Type { get; } - /// - /// The specific media type (MIME type) for a content resource. - /// See format - /// - /// Only content resources may have the Format property - [JsonProperty(Order = 20)] - public string? Format { get; set; } + /// + /// The specific media type (MIME type) for a content resource. + /// See format + /// + /// Only content resources may have the Format property + [JsonProperty(Order = 20)] + public string? Format { get; set; } - /// - /// The language or languages used in the content of this external resource. - /// See language - /// - [JsonProperty(Order = 21)] - public List? Language { get; set; } + /// + /// The language or languages used in the content of this external resource. + /// See language + /// + [JsonProperty(Order = 21)] + public List? Language { get; set; } - [JsonProperty(Order = 900)] - public List? Annotations { get; set; } - } -} + [JsonProperty(Order = 900)] public List? Annotations { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Content/ISpatial.cs b/src/IIIF/IIIF/Presentation/V3/Content/ISpatial.cs index dc6edd5..baebc65 100644 --- a/src/IIIF/IIIF/Presentation/V3/Content/ISpatial.cs +++ b/src/IIIF/IIIF/Presentation/V3/Content/ISpatial.cs @@ -1,11 +1,10 @@ -namespace IIIF.Presentation.V3.Content +namespace IIIF.Presentation.V3.Content; + +/// +/// Represents a 2 dimensional resource. +/// +public interface ISpatial { - /// - /// Represents a 2 dimensional resource. - /// - public interface ISpatial - { - int? Width { get; set; } - int? Height { get; set; } - } -} + int? Width { get; set; } + int? Height { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Content/ITemporal.cs b/src/IIIF/IIIF/Presentation/V3/Content/ITemporal.cs index 3a47411..b9dab66 100644 --- a/src/IIIF/IIIF/Presentation/V3/Content/ITemporal.cs +++ b/src/IIIF/IIIF/Presentation/V3/Content/ITemporal.cs @@ -1,10 +1,9 @@ -namespace IIIF.Presentation.V3.Content +namespace IIIF.Presentation.V3.Content; + +/// +/// Represents a time-based resource. +/// +public interface ITemporal { - /// - /// Represents a time-based resource. - /// - public interface ITemporal - { - public double? Duration { get; set; } - } -} + public double? Duration { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Content/Image.cs b/src/IIIF/IIIF/Presentation/V3/Content/Image.cs index 76d8205..2f6c7f4 100644 --- a/src/IIIF/IIIF/Presentation/V3/Content/Image.cs +++ b/src/IIIF/IIIF/Presentation/V3/Content/Image.cs @@ -1,16 +1,15 @@ using IIIF.Presentation.V3.Annotation; using Newtonsoft.Json; -namespace IIIF.Presentation.V3.Content +namespace IIIF.Presentation.V3.Content; + +public class Image : ExternalResource, ISpatial, IPaintable { - public class Image : ExternalResource, ISpatial, IPaintable - { - [JsonProperty(Order = 11)] - public int? Width { get; set; } - - [JsonProperty(Order = 12)] - public int? Height { get; set; } + [JsonProperty(Order = 11)] public int? Width { get; set; } - public Image() : base(nameof(Image)) {} + [JsonProperty(Order = 12)] public int? Height { get; set; } + + public Image() : base(nameof(Image)) + { } -} +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Content/Video.cs b/src/IIIF/IIIF/Presentation/V3/Content/Video.cs index 41b0658..c8c9ecc 100644 --- a/src/IIIF/IIIF/Presentation/V3/Content/Video.cs +++ b/src/IIIF/IIIF/Presentation/V3/Content/Video.cs @@ -1,13 +1,14 @@ using IIIF.Presentation.V3.Annotation; -namespace IIIF.Presentation.V3.Content +namespace IIIF.Presentation.V3.Content; + +public class Video : ExternalResource, ISpatial, ITemporal, IPaintable { - public class Video : ExternalResource, ISpatial, ITemporal, IPaintable - { - public int? Width { get; set; } - public int? Height { get; set; } - public double? Duration { get; set; } + public int? Width { get; set; } + public int? Height { get; set; } + public double? Duration { get; set; } - public Video() : base(nameof(Video)) { } + public Video() : base(nameof(Video)) + { } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/ICollectionItem.cs b/src/IIIF/IIIF/Presentation/V3/ICollectionItem.cs index 35aff20..7da67c3 100644 --- a/src/IIIF/IIIF/Presentation/V3/ICollectionItem.cs +++ b/src/IIIF/IIIF/Presentation/V3/ICollectionItem.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; -namespace IIIF.Presentation.V3 +namespace IIIF.Presentation.V3; + +/// +/// Marker interface for resources that can be in a Collection's Items property. +/// +public interface ICollectionItem { - /// - /// Marker interface for resources that can be in a Collection's Items property. - /// - public interface ICollectionItem - { - List? Services { get; set; } - } -} + List? Services { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/IStructuralLocation.cs b/src/IIIF/IIIF/Presentation/V3/IStructuralLocation.cs index f55b0c2..1a7c07a 100644 --- a/src/IIIF/IIIF/Presentation/V3/IStructuralLocation.cs +++ b/src/IIIF/IIIF/Presentation/V3/IStructuralLocation.cs @@ -1,11 +1,9 @@ - -namespace IIIF.Presentation.V3 +namespace IIIF.Presentation.V3; + +/// +/// Represents a IIIF resource that can be "pointed at". +/// TODO - see how this connects with Content State +/// +public interface IStructuralLocation { - /// - /// Represents a IIIF resource that can be "pointed at". - /// TODO - see how this connects with Content State - /// - public interface IStructuralLocation - { - } -} +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Manifest.cs b/src/IIIF/IIIF/Presentation/V3/Manifest.cs index 5d155df..a1845bc 100644 --- a/src/IIIF/IIIF/Presentation/V3/Manifest.cs +++ b/src/IIIF/IIIF/Presentation/V3/Manifest.cs @@ -1,34 +1,31 @@ using System.Collections.Generic; using Newtonsoft.Json; -namespace IIIF.Presentation.V3 +namespace IIIF.Presentation.V3; + +public class Manifest : StructureBase, ICollectionItem { - public class Manifest : StructureBase, ICollectionItem - { - public override string Type => nameof(Manifest); - - [JsonProperty(Order = 300)] - public List? Items { get; set; } - - [JsonProperty(Order = 400)] - public List? Structures { get; set; } - - /// - /// The direction in which a set of Canvases should be displayed to the user - /// See viewingdirection - /// - /// - [JsonProperty(Order = 32)] - public string? ViewingDirection { get; set; } - - [JsonProperty(Order = 35)] - // TODO - Interface may cause issues for deserialization - public IStructuralLocation? Start { get; set; } - - /// - /// Note that this is not the same as ResourceBase::Service - /// - [JsonProperty(Order = 39)] - public List? Services { get; set; } - } -} + public override string Type => nameof(Manifest); + + [JsonProperty(Order = 300)] public List? Items { get; set; } + + [JsonProperty(Order = 400)] public List? Structures { get; set; } + + /// + /// The direction in which a set of Canvases should be displayed to the user + /// See viewingdirection + /// + /// + [JsonProperty(Order = 32)] + public string? ViewingDirection { get; set; } + + [JsonProperty(Order = 35)] + // TODO - Interface may cause issues for deserialization + public IStructuralLocation? Start { get; set; } + + /// + /// Note that this is not the same as ResourceBase::Service + /// + [JsonProperty(Order = 39)] + public List? Services { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/ManifestX.cs b/src/IIIF/IIIF/Presentation/V3/ManifestX.cs index 77cc5dc..205b3ba 100644 --- a/src/IIIF/IIIF/Presentation/V3/ManifestX.cs +++ b/src/IIIF/IIIF/Presentation/V3/ManifestX.cs @@ -1,16 +1,17 @@ -namespace IIIF.Presentation.V3 +namespace IIIF.Presentation.V3; + +/// +/// Extension methods for working with objects. +/// +public static class ManifestX { /// - /// Extension methods for working with objects. + /// Check if this Manifest contains any AV items. /// - public static class ManifestX + /// + /// True if .Items contains a Canvas with a Duration of greater than 0. + public static bool ContainsAV(this Manifest manifest) { - /// - /// Check if this Manifest contains any AV items. - /// - /// - /// True if .Items contains a Canvas with a Duration of greater than 0. - public static bool ContainsAV(this Manifest manifest) - => manifest.Items?.Exists(i => (i.Duration ?? 0) > 0) ?? false; + return manifest.Items?.Exists(i => (i.Duration ?? 0) > 0) ?? false; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Range.cs b/src/IIIF/IIIF/Presentation/V3/Range.cs index cf553c4..15a8ad7 100644 --- a/src/IIIF/IIIF/Presentation/V3/Range.cs +++ b/src/IIIF/IIIF/Presentation/V3/Range.cs @@ -2,22 +2,17 @@ using IIIF.Presentation.V3.Annotation; using Newtonsoft.Json; -namespace IIIF.Presentation.V3 +namespace IIIF.Presentation.V3; + +public class Range : StructureBase, IStructuralLocation { - public class Range : StructureBase, IStructuralLocation - { - public override string Type => nameof(Range); - - [JsonProperty(Order = 32)] - public string? ViewingDirection { get; set; } - - [JsonProperty(Order = 35)] - public IStructuralLocation? Start { get; set; } - - [JsonProperty(Order = 300)] - public List? Items { get; set; } - - [JsonProperty(Order = 400)] - public AnnotationCollection? Supplementary { get; set; } - } -} + public override string Type => nameof(Range); + + [JsonProperty(Order = 32)] public string? ViewingDirection { get; set; } + + [JsonProperty(Order = 35)] public IStructuralLocation? Start { get; set; } + + [JsonProperty(Order = 300)] public List? Items { get; set; } + + [JsonProperty(Order = 400)] public AnnotationCollection? Supplementary { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/ResourceBase.cs b/src/IIIF/IIIF/Presentation/V3/ResourceBase.cs index 94dd13f..c66070a 100644 --- a/src/IIIF/IIIF/Presentation/V3/ResourceBase.cs +++ b/src/IIIF/IIIF/Presentation/V3/ResourceBase.cs @@ -3,123 +3,121 @@ using IIIF.Presentation.V3.Strings; using Newtonsoft.Json; -namespace IIIF.Presentation.V3 +namespace IIIF.Presentation.V3; + +/// +/// Base class for all IIIF presentation resources. +/// +public abstract class ResourceBase : JsonLdBase, IService { /// - /// Base class for all IIIF presentation resources. + /// The URI that identifies the resource. + /// See id + /// + [JsonProperty(Order = 2)] + public string? Id { get; set; } + + /// + /// The type or class of the resource. + /// See type + /// + [JsonProperty(Order = 3)] + public abstract string Type { get; } + + /// + /// A schema or named set of functionality available from the resource. + /// The profile can further clarify the type and/or format of an external resource or service, + /// allowing clients to customize their handling of the resource that has the profile property. + /// See profile + /// + [JsonProperty(Order = 4)] + public string? Profile { get; set; } + + /// + /// A human readable label, name or title. + /// See Label + /// + [JsonProperty(Order = 10)] + public LanguageMap? Label { get; set; } + + /// + /// A short textual summary intended to be conveyed to the user when the metadata entries for the resource are + /// not being displayed. + /// See summary + /// + [JsonProperty(Order = 15)] + public LanguageMap? Summary { get; set; } + + /// + /// A content resource that represents the resource. + /// See thumbnail + /// + [JsonProperty(Order = 16)] + public List? Thumbnail { get; set; } + + /// + /// A web page that is about the object represented by this resource. + /// See homepage + /// + [JsonProperty(Order = 17)] + public List? Homepage { get; set; } + + /// + /// An ordered list of descriptions to be displayed to the user when they interact with the resource. + /// See metadata + /// + [JsonProperty(Order = 18)] + public List? Metadata { get; set; } + + /// + /// A string that identifies a license or rights statement that applies to the content of the resource. + /// See rights + /// + [JsonProperty(Order = 19)] + public string? Rights { get; set; } + + /// + /// Text that must be displayed when the resource is displayed or used. + /// See requiredstatement + /// + [JsonProperty(Order = 20)] + public LabelValuePair? RequiredStatement { get; set; } + + /// + /// An organization or person that contributed to providing the content of the resource. + /// See provider + /// + [JsonProperty(Order = 21)] + public List? Provider { get; set; } + + /// + /// A resource that is an alternative, non-IIIF representation of the resource. + /// See rendering + /// + [JsonProperty(Order = 22)] + public List? Rendering { get; set; } + + /// + /// A machine-readable resource such as an XML or RDF description that is related to the current resource. + /// See seealso + /// + [JsonProperty(Order = 24)] + public List? SeeAlso { get; set; } + + [JsonProperty(Order = 28)] public List? Service { get; set; } + + /// + /// A set of user experience features that the publisher of the content would prefer the client to use when + /// presenting the resource. + /// See behavior + /// + [JsonProperty(Order = 31)] + public List? Behavior { get; set; } + + /// + /// A containing resource that includes this resource. + /// See partof /// - public abstract class ResourceBase : JsonLdBase, IService - { - /// - /// The URI that identifies the resource. - /// See id - /// - [JsonProperty(Order = 2)] - public string? Id { get; set; } - - /// - /// The type or class of the resource. - /// See type - /// - [JsonProperty(Order = 3)] - public abstract string Type { get; } - - /// - /// A schema or named set of functionality available from the resource. - /// The profile can further clarify the type and/or format of an external resource or service, - /// allowing clients to customize their handling of the resource that has the profile property. - /// See profile - /// - [JsonProperty(Order = 4)] - public string? Profile { get; set; } - - /// - /// A human readable label, name or title. - /// See Label - /// - [JsonProperty(Order = 10)] - public LanguageMap? Label { get; set; } - - /// - /// A short textual summary intended to be conveyed to the user when the metadata entries for the resource are - /// not being displayed. - /// See summary - /// - [JsonProperty(Order = 15)] - public LanguageMap? Summary { get; set; } - - /// - /// A content resource that represents the resource. - /// See thumbnail - /// - [JsonProperty(Order = 16)] - public List? Thumbnail { get; set; } - - /// - /// A web page that is about the object represented by this resource. - /// See homepage - /// - [JsonProperty(Order = 17)] - public List? Homepage { get; set; } - - /// - /// An ordered list of descriptions to be displayed to the user when they interact with the resource. - /// See metadata - /// - [JsonProperty(Order = 18)] - public List? Metadata { get; set; } - - /// - /// A string that identifies a license or rights statement that applies to the content of the resource. - /// See rights - /// - [JsonProperty(Order = 19)] - public string? Rights { get; set; } - - /// - /// Text that must be displayed when the resource is displayed or used. - /// See requiredstatement - /// - [JsonProperty(Order = 20)] - public LabelValuePair? RequiredStatement { get; set; } - - /// - /// An organization or person that contributed to providing the content of the resource. - /// See provider - /// - [JsonProperty(Order = 21)] - public List? Provider { get; set; } - - /// - /// A resource that is an alternative, non-IIIF representation of the resource. - /// See rendering - /// - [JsonProperty(Order = 22)] - public List? Rendering { get; set; } - - /// - /// A machine-readable resource such as an XML or RDF description that is related to the current resource. - /// See seealso - /// - [JsonProperty(Order = 24)] - public List? SeeAlso { get; set; } - - [JsonProperty(Order = 28)] - public List? Service { get; set; } - - /// - /// A set of user experience features that the publisher of the content would prefer the client to use when - /// presenting the resource. - /// See behavior - /// - [JsonProperty(Order = 31)] - public List? Behavior { get; set; } - - /// - /// A containing resource that includes this resource. - /// See partof - /// - [JsonProperty(Order = 1000)] - public List? PartOf { get; set; } - } -} + [JsonProperty(Order = 1000)] + public List? PartOf { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Selectors/AudioContentSelector.cs b/src/IIIF/IIIF/Presentation/V3/Selectors/AudioContentSelector.cs index 9fbbef9..5dc4057 100644 --- a/src/IIIF/IIIF/Presentation/V3/Selectors/AudioContentSelector.cs +++ b/src/IIIF/IIIF/Presentation/V3/Selectors/AudioContentSelector.cs @@ -1,8 +1,6 @@ - -namespace IIIF.Presentation.V3.Selectors +namespace IIIF.Presentation.V3.Selectors; + +public class AudioContentSelector : ISelector { - public class AudioContentSelector : ISelector - { - public string? Type => nameof(AudioContentSelector); - } -} + public string? Type => nameof(AudioContentSelector); +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Selectors/ISelector.cs b/src/IIIF/IIIF/Presentation/V3/Selectors/ISelector.cs index 5d89b97..5a9107a 100644 --- a/src/IIIF/IIIF/Presentation/V3/Selectors/ISelector.cs +++ b/src/IIIF/IIIF/Presentation/V3/Selectors/ISelector.cs @@ -1,7 +1,6 @@ -namespace IIIF.Presentation.V3.Selectors +namespace IIIF.Presentation.V3.Selectors; + +public interface ISelector { - public interface ISelector - { - string? Type { get; } - } + string? Type { get; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Selectors/ImageApiSelector.cs b/src/IIIF/IIIF/Presentation/V3/Selectors/ImageApiSelector.cs index 759e142..6649305 100644 --- a/src/IIIF/IIIF/Presentation/V3/Selectors/ImageApiSelector.cs +++ b/src/IIIF/IIIF/Presentation/V3/Selectors/ImageApiSelector.cs @@ -1,13 +1,11 @@ - -namespace IIIF.Presentation.V3.Selectors +namespace IIIF.Presentation.V3.Selectors; + +public class ImageApiSelector : ISelector { - public class ImageApiSelector : ISelector - { - public string? Type => nameof(ImageApiSelector); - public string? Region { get; set; } - public string? Size { get; set; } - public string? Rotation { get; set; } - public string? Quality { get; set; } - public string? Format { get; set; } - } -} + public string? Type => nameof(ImageApiSelector); + public string? Region { get; set; } + public string? Size { get; set; } + public string? Rotation { get; set; } + public string? Quality { get; set; } + public string? Format { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Selectors/PointSelector.cs b/src/IIIF/IIIF/Presentation/V3/Selectors/PointSelector.cs index a7b2ed3..0b86218 100644 --- a/src/IIIF/IIIF/Presentation/V3/Selectors/PointSelector.cs +++ b/src/IIIF/IIIF/Presentation/V3/Selectors/PointSelector.cs @@ -1,10 +1,9 @@ -namespace IIIF.Presentation.V3.Selectors +namespace IIIF.Presentation.V3.Selectors; + +public class PointSelector : ISelector { - public class PointSelector : ISelector - { - public string? Type => nameof(PointSelector); - public int? X { get; set; } - public int? Y { get; set; } - public double? T { get; set; } - } -} + public string? Type => nameof(PointSelector); + public int? X { get; set; } + public int? Y { get; set; } + public double? T { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Selectors/VideoContentSelector.cs b/src/IIIF/IIIF/Presentation/V3/Selectors/VideoContentSelector.cs index 1c92455..fab215d 100644 --- a/src/IIIF/IIIF/Presentation/V3/Selectors/VideoContentSelector.cs +++ b/src/IIIF/IIIF/Presentation/V3/Selectors/VideoContentSelector.cs @@ -1,8 +1,6 @@ - -namespace IIIF.Presentation.V3.Selectors +namespace IIIF.Presentation.V3.Selectors; + +public class VideoContentSelector : ISelector { - public class VideoContentSelector : ISelector - { - public string? Type => nameof(VideoContentSelector); - } -} + public string? Type => nameof(VideoContentSelector); +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/SpecificResource.cs b/src/IIIF/IIIF/Presentation/V3/SpecificResource.cs index 7dd9b21..3166733 100644 --- a/src/IIIF/IIIF/Presentation/V3/SpecificResource.cs +++ b/src/IIIF/IIIF/Presentation/V3/SpecificResource.cs @@ -1,16 +1,13 @@ using IIIF.Presentation.V3.Selectors; using Newtonsoft.Json; -namespace IIIF.Presentation.V3 +namespace IIIF.Presentation.V3; + +public class SpecificResource : ResourceBase, IStructuralLocation { - public class SpecificResource : ResourceBase, IStructuralLocation - { - public override string Type => nameof(SpecificResource); - - [JsonProperty(Order = 101)] - public string Source { get; set; } - - [JsonProperty(Order = 102)] - public ISelector Selector { get; set; } - } -} + public override string Type => nameof(SpecificResource); + + [JsonProperty(Order = 101)] public string Source { get; set; } + + [JsonProperty(Order = 102)] public ISelector Selector { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Strings/LabelValuePair.cs b/src/IIIF/IIIF/Presentation/V3/Strings/LabelValuePair.cs index 3e142c8..58ee501 100644 --- a/src/IIIF/IIIF/Presentation/V3/Strings/LabelValuePair.cs +++ b/src/IIIF/IIIF/Presentation/V3/Strings/LabelValuePair.cs @@ -1,42 +1,46 @@ using System.Linq; using Newtonsoft.Json; -namespace IIIF.Presentation.V3.Strings +namespace IIIF.Presentation.V3.Strings; + +public class LabelValuePair { - public class LabelValuePair + private LanguageMap valueMap; + private LanguageMap labelMap; + + /// + /// This helper method can take any number of values, but will only add them if not empty. + /// It _will_ add whitespace strings, though - you might want that. + /// + /// + /// + /// + public LabelValuePair(string language, string label, params string?[] values) { - private LanguageMap valueMap; - private LanguageMap labelMap; - - /// - /// This helper method can take any number of values, but will only add them if not empty. - /// It _will_ add whitespace strings, though - you might want that. - /// - /// - /// - /// - public LabelValuePair(string language, string label, params string?[] values) - { - var actualValues = values - .Where(v => !string.IsNullOrEmpty(v)) - .ToList(); - labelMap = new LanguageMap(language, label); - valueMap = new LanguageMap(language, actualValues.FirstOrDefault()); - foreach (var value in actualValues.Skip(1)) - { - valueMap[language].Add(value); - } - } - - [JsonConstructor] - public LabelValuePair(LanguageMap label, LanguageMap value) - { - labelMap = label; - valueMap = value; - } + var actualValues = values + .Where(v => !string.IsNullOrEmpty(v)) + .ToList(); + labelMap = new LanguageMap(language, label); + valueMap = new LanguageMap(language, actualValues.FirstOrDefault()); + foreach (var value in actualValues.Skip(1)) valueMap[language].Add(value); + } - public LanguageMap Label { get => labelMap; set => labelMap = value; } - public LanguageMap Value { get => valueMap; set => valueMap = value; } + [JsonConstructor] + public LabelValuePair(LanguageMap label, LanguageMap value) + { + labelMap = label; + valueMap = value; } -} + public LanguageMap Label + { + get => labelMap; + set => labelMap = value; + } + + public LanguageMap Value + { + get => valueMap; + set => valueMap = value; + } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/Strings/LanguageMap.cs b/src/IIIF/IIIF/Presentation/V3/Strings/LanguageMap.cs index ae269ba..77a086e 100644 --- a/src/IIIF/IIIF/Presentation/V3/Strings/LanguageMap.cs +++ b/src/IIIF/IIIF/Presentation/V3/Strings/LanguageMap.cs @@ -5,43 +5,40 @@ using IIIF.Serialisation; using Newtonsoft.Json; -namespace IIIF.Presentation.V3.Strings +namespace IIIF.Presentation.V3.Strings; + +[JsonConverter(typeof(LanguageMapSerialiser))] +public class LanguageMap : Dictionary> { - [JsonConverter(typeof(LanguageMapSerialiser))] - public class LanguageMap : Dictionary> + public LanguageMap() { - public LanguageMap() { } + } - public LanguageMap(string language, string singleValue) - { - this[language] = new List { singleValue }; - } - - public LanguageMap(string language, IEnumerable values) - { - this[language] = values.ToList(); - } + public LanguageMap(string language, string singleValue) + { + this[language] = new List { singleValue }; + } - public override string ToString() - { - StringBuilder sb = new(); - foreach (List value in Values) - { - foreach (string s in value) - { - if (sb.Length > 0) - { - sb.AppendLine(); - } - sb.Append(s); - } - } - return sb.ToString(); - } + public LanguageMap(string language, IEnumerable values) + { + this[language] = values.ToList(); + } - public string Join(string separator) + public override string ToString() + { + StringBuilder sb = new(); + foreach (List value in Values) + foreach (var s in value) { - return String.Join(separator, Values.SelectMany(s => s.ToList())); + if (sb.Length > 0) sb.AppendLine(); + sb.Append(s); } + + return sb.ToString(); + } + + public string Join(string separator) + { + return string.Join(separator, Values.SelectMany(s => s.ToList())); } -} +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/V3/StructureBase.cs b/src/IIIF/IIIF/Presentation/V3/StructureBase.cs index ce4a0dd..d4c5630 100644 --- a/src/IIIF/IIIF/Presentation/V3/StructureBase.cs +++ b/src/IIIF/IIIF/Presentation/V3/StructureBase.cs @@ -3,55 +3,53 @@ using IIIF.Presentation.V3.Annotation; using Newtonsoft.Json; -namespace IIIF.Presentation.V3 +namespace IIIF.Presentation.V3; + +/// +/// Base class for resources that form the structure of a IIIF resource: +/// Manifest, Canvas, Collection, Range +/// See Defined Types for more information. +/// +public abstract class StructureBase : ResourceBase { + private DateTime? navDateInternal; + /// - /// Base class for resources that form the structure of a IIIF resource: - /// Manifest, Canvas, Collection, Range - /// See Defined Types for more information. + /// You can't always set a navDate as a DateTime/ + /// Not serialised. /// - public abstract class StructureBase : ResourceBase + [JsonIgnore] + public DateTime? NavDateDateTime { - private DateTime? navDateInternal; - - /// - /// You can't always set a navDate as a DateTime/ - /// Not serialised. - /// - [JsonIgnore] - public DateTime? NavDateDateTime + get => navDateInternal; + set { - get => navDateInternal; - set - { - navDateInternal = value; - NavDate = navDateInternal?.ToString("o"); - } + navDateInternal = value; + NavDate = navDateInternal?.ToString("o"); } - - /// - /// A date that clients may use for navigation purposes. - /// See navDate - /// - /// This can still be set manually - [JsonProperty(Order = 41)] - public string? NavDate { get; set; } - - /// - /// A single Canvas that provides additional content for use before the main content. - /// See placeholdercanvas - /// - [JsonProperty(Order = 51)] - public Canvas? PlaceholderCanvas { get; set; } - - /// - /// A single Canvas that provides additional content for use while rendering the resource. - /// See accompanyingcanvas - /// - [JsonProperty(Order = 52)] - public Canvas? AccompanyingCanvas { get; set; } - - [JsonProperty(Order = 900)] - public List? Annotations { get; set; } } -} + + /// + /// A date that clients may use for navigation purposes. + /// See navDate + /// + /// This can still be set manually + [JsonProperty(Order = 41)] + public string? NavDate { get; set; } + + /// + /// A single Canvas that provides additional content for use before the main content. + /// See placeholdercanvas + /// + [JsonProperty(Order = 51)] + public Canvas? PlaceholderCanvas { get; set; } + + /// + /// A single Canvas that provides additional content for use while rendering the resource. + /// See accompanyingcanvas + /// + [JsonProperty(Order = 52)] + public Canvas? AccompanyingCanvas { get; set; } + + [JsonProperty(Order = 900)] public List? Annotations { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Presentation/Version.cs b/src/IIIF/IIIF/Presentation/Version.cs index e63cb56..823b246 100644 --- a/src/IIIF/IIIF/Presentation/Version.cs +++ b/src/IIIF/IIIF/Presentation/Version.cs @@ -1,28 +1,26 @@ using System.ComponentModel.DataAnnotations; -namespace IIIF.Presentation +namespace IIIF.Presentation; + +/// +/// Available IIIF presentation API Versions. +/// +public enum Version { /// - /// Available IIIF presentation API Versions. + /// Fallback value, unknown version. + /// + [Display(Description = "Unknown")] Unknown = 0, + + /// + /// IIIF Presentation version 2. + /// + [Display(Description = Context.Presentation2Context)] + V2, + + /// + /// IIIF Presentation version 3. /// - public enum Version - { - /// - /// Fallback value, unknown version. - /// - [Display(Description = "Unknown")] - Unknown = 0, - - /// - /// IIIF Presentation version 2. - /// - [Display(Description = Context.Presentation2Context)] - V2, - - /// - /// IIIF Presentation version 3. - /// - [Display(Description = Context.Presentation3Context)] - V3 - } + [Display(Description = Context.Presentation3Context)] + V3 } \ No newline at end of file diff --git a/src/IIIF/IIIF/Search/IHasIgnorableParameters.cs b/src/IIIF/IIIF/Search/IHasIgnorableParameters.cs index f753ce9..8fc6ed7 100644 --- a/src/IIIF/IIIF/Search/IHasIgnorableParameters.cs +++ b/src/IIIF/IIIF/Search/IHasIgnorableParameters.cs @@ -1,7 +1,6 @@ -namespace IIIF.Search +namespace IIIF.Search; + +public interface IHasIgnorableParameters { - public interface IHasIgnorableParameters - { - string[]? Ignored { get; set; } - } -} + string[]? Ignored { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Search/V1/AutoCompleteService.cs b/src/IIIF/IIIF/Search/V1/AutoCompleteService.cs index c03ad55..6cf52f4 100644 --- a/src/IIIF/IIIF/Search/V1/AutoCompleteService.cs +++ b/src/IIIF/IIIF/Search/V1/AutoCompleteService.cs @@ -1,13 +1,12 @@ using IIIF.Presentation.V2; using Newtonsoft.Json; -namespace IIIF.Search.V1 +namespace IIIF.Search.V1; + +public class AutoCompleteService : ResourceBase, IService { - public class AutoCompleteService : ResourceBase, IService - { - public const string AutoCompleteService1Profile = "http://iiif.io/api/search/1/autocomplete"; + public const string AutoCompleteService1Profile = "http://iiif.io/api/search/1/autocomplete"; - [JsonProperty(PropertyName = "@type", Order = 3)] - public override string? Type { get; set; } = "AutoCompleteService1"; - } -} + [JsonProperty(PropertyName = "@type", Order = 3)] + public override string? Type { get; set; } = "AutoCompleteService1"; +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Search/V1/Hit.cs b/src/IIIF/IIIF/Search/V1/Hit.cs index abf287d..ff2aeb9 100644 --- a/src/IIIF/IIIF/Search/V1/Hit.cs +++ b/src/IIIF/IIIF/Search/V1/Hit.cs @@ -1,29 +1,28 @@ using IIIF.Presentation.V2; using Newtonsoft.Json; -namespace IIIF.Search.V1 +namespace IIIF.Search.V1; + +public class Hit : ResourceBase { - public class Hit : ResourceBase + public override string? Type { - public override string? Type - { - get => "search:Hit"; - set => throw new System.NotImplementedException(); - } + get => "search:Hit"; + set => throw new System.NotImplementedException(); + } - [JsonProperty(Order = 40, PropertyName = "annotations")] - public string[]? Annotations { get; set; } + [JsonProperty(Order = 40, PropertyName = "annotations")] + public string[]? Annotations { get; set; } - [JsonProperty(Order = 50, PropertyName = "match")] - public string? Match { get; set; } + [JsonProperty(Order = 50, PropertyName = "match")] + public string? Match { get; set; } - [JsonProperty(Order = 51, PropertyName = "before")] - public string? Before { get; set; } + [JsonProperty(Order = 51, PropertyName = "before")] + public string? Before { get; set; } - [JsonProperty(Order = 52, PropertyName = "after")] - public string? After { get; set; } + [JsonProperty(Order = 52, PropertyName = "after")] + public string? After { get; set; } - [JsonProperty(Order = 60, PropertyName = "selectors")] - public TextQuoteSelector[]? Selectors { get; set; } - } -} + [JsonProperty(Order = 60, PropertyName = "selectors")] + public TextQuoteSelector[]? Selectors { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Search/V1/SearchResultAnnotationList.cs b/src/IIIF/IIIF/Search/V1/SearchResultAnnotationList.cs index 3ba3103..65c29c0 100644 --- a/src/IIIF/IIIF/Search/V1/SearchResultAnnotationList.cs +++ b/src/IIIF/IIIF/Search/V1/SearchResultAnnotationList.cs @@ -2,23 +2,22 @@ using IIIF.Presentation.V2.Annotation; using Newtonsoft.Json; -namespace IIIF.Search.V1 +namespace IIIF.Search.V1; + +public class SearchResultAnnotationList : AnnotationList { - public class SearchResultAnnotationList : AnnotationList - { - [JsonProperty(Order = 10, PropertyName = "within")] - public SearchResultsLayer? Within { get; set; } + [JsonProperty(Order = 10, PropertyName = "within")] + public SearchResultsLayer? Within { get; set; } - [JsonProperty(Order = 12, PropertyName = "previous")] - public string? Previous { get; set; } + [JsonProperty(Order = 12, PropertyName = "previous")] + public string? Previous { get; set; } - [JsonProperty(Order = 13, PropertyName = "next")] - public string? Next { get; set; } + [JsonProperty(Order = 13, PropertyName = "next")] + public string? Next { get; set; } - [JsonProperty(Order = 16, PropertyName = "startIndex")] - public int? StartIndex { get; set; } + [JsonProperty(Order = 16, PropertyName = "startIndex")] + public int? StartIndex { get; set; } - [JsonProperty(Order = 30, PropertyName = "hits")] - public Hit[]? Hits { get; set; } - } -} + [JsonProperty(Order = 30, PropertyName = "hits")] + public Hit[]? Hits { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Search/V1/SearchResultsLayer.cs b/src/IIIF/IIIF/Search/V1/SearchResultsLayer.cs index 488c9a8..9474f51 100644 --- a/src/IIIF/IIIF/Search/V1/SearchResultsLayer.cs +++ b/src/IIIF/IIIF/Search/V1/SearchResultsLayer.cs @@ -1,29 +1,28 @@ using IIIF.Presentation.V2; using Newtonsoft.Json; -namespace IIIF.Search.V1 -{ - /// - /// Each AnnotationList references a Layer. The Layer can be a blank node, and must be included in every annotation list. - /// - public class SearchResultsLayer : ResourceBase, IHasIgnorableParameters +namespace IIIF.Search.V1; + +/// +/// Each AnnotationList references a Layer. The Layer can be a blank node, and must be included in every annotation list. +/// +public class SearchResultsLayer : ResourceBase, IHasIgnorableParameters +{ + public override string? Type { - public override string? Type - { - get => "sc:Layer"; - set => throw new System.NotImplementedException(); - } + get => "sc:Layer"; + set => throw new System.NotImplementedException(); + } - [JsonProperty(Order = 10, PropertyName = "total")] - public int? Total { get; set; } + [JsonProperty(Order = 10, PropertyName = "total")] + public int? Total { get; set; } - [JsonProperty(Order = 14, PropertyName = "first")] - public string? First { get; set; } + [JsonProperty(Order = 14, PropertyName = "first")] + public string? First { get; set; } - [JsonProperty(Order = 15, PropertyName = "last")] - public string? Last { get; set; } + [JsonProperty(Order = 15, PropertyName = "last")] + public string? Last { get; set; } - [JsonProperty(Order = 20, PropertyName = "ignored")] - public string[]? Ignored { get; set; } - } -} + [JsonProperty(Order = 20, PropertyName = "ignored")] + public string[]? Ignored { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Search/V1/SearchService.cs b/src/IIIF/IIIF/Search/V1/SearchService.cs index 3bcc39d..2a89039 100644 --- a/src/IIIF/IIIF/Search/V1/SearchService.cs +++ b/src/IIIF/IIIF/Search/V1/SearchService.cs @@ -1,17 +1,15 @@ using IIIF.Presentation.V2; using Newtonsoft.Json; -namespace IIIF.Search.V1 +namespace IIIF.Search.V1; + +public class SearchService : ResourceBase, IService { - public class SearchService : ResourceBase, IService - { - public const string Search1Context = "http://iiif.io/api/search/1/context.json"; - public const string Search1Profile = "http://iiif.io/api/search/1/search"; + public const string Search1Context = "http://iiif.io/api/search/1/context.json"; + public const string Search1Profile = "http://iiif.io/api/search/1/search"; - [JsonProperty(PropertyName = "@type", Order = 3)] - public override string? Type { get; set; } = "SearchService1"; + [JsonProperty(PropertyName = "@type", Order = 3)] + public override string? Type { get; set; } = "SearchService1"; - [JsonProperty(Order = 28)] - public AutoCompleteService? Service { get; set; } - } -} + [JsonProperty(Order = 28)] public AutoCompleteService? Service { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Search/V1/Term.cs b/src/IIIF/IIIF/Search/V1/Term.cs index e32f88b..1b5b4ab 100644 --- a/src/IIIF/IIIF/Search/V1/Term.cs +++ b/src/IIIF/IIIF/Search/V1/Term.cs @@ -1,20 +1,19 @@ using Newtonsoft.Json; -namespace IIIF.Search.V1 +namespace IIIF.Search.V1; + +public class Term { - public class Term - { - [JsonProperty(Order = 11, PropertyName = "match")] - public string? Match { get; set; } + [JsonProperty(Order = 11, PropertyName = "match")] + public string? Match { get; set; } - // Hide for now, Wellcome don't know this - //[JsonProperty(Order = 12, PropertyName = "total")] - //public int Total { get; set; } + // Hide for now, Wellcome don't know this + //[JsonProperty(Order = 12, PropertyName = "total")] + //public int Total { get; set; } - [JsonProperty(Order = 13, PropertyName = "label")] - public string? Label { get; set; } + [JsonProperty(Order = 13, PropertyName = "label")] + public string? Label { get; set; } - [JsonProperty(Order = 14, PropertyName = "search")] - public string? Search { get; set; } - } -} + [JsonProperty(Order = 14, PropertyName = "search")] + public string? Search { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Search/V1/TermList.cs b/src/IIIF/IIIF/Search/V1/TermList.cs index a833061..472b735 100644 --- a/src/IIIF/IIIF/Search/V1/TermList.cs +++ b/src/IIIF/IIIF/Search/V1/TermList.cs @@ -1,20 +1,19 @@ using IIIF.Presentation.V2; using Newtonsoft.Json; -namespace IIIF.Search.V1 +namespace IIIF.Search.V1; + +public class TermList : ResourceBase, IHasIgnorableParameters { - public class TermList : ResourceBase, IHasIgnorableParameters + public override string? Type { - public override string? Type - { - get => "search:TermList"; - set => throw new System.NotImplementedException(); - } + get => "search:TermList"; + set => throw new System.NotImplementedException(); + } - [JsonProperty(Order = 20, PropertyName = "ignored")] - public string[]? Ignored { get; set; } + [JsonProperty(Order = 20, PropertyName = "ignored")] + public string[]? Ignored { get; set; } - [JsonProperty(Order = 40, PropertyName = "terms")] - public Term[]? Terms { get; set; } - } + [JsonProperty(Order = 40, PropertyName = "terms")] + public Term[]? Terms { get; set; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Search/V1/TextQuoteSelector.cs b/src/IIIF/IIIF/Search/V1/TextQuoteSelector.cs index 15000e6..492a72d 100644 --- a/src/IIIF/IIIF/Search/V1/TextQuoteSelector.cs +++ b/src/IIIF/IIIF/Search/V1/TextQuoteSelector.cs @@ -1,23 +1,22 @@ using IIIF.Presentation.V2; using Newtonsoft.Json; -namespace IIIF.Search.V1 +namespace IIIF.Search.V1; + +public class TextQuoteSelector : ResourceBase { - public class TextQuoteSelector: ResourceBase + public override string? Type { - public override string? Type - { - get => "oa:TextQuoteSelector"; - set => throw new System.NotImplementedException(); - } + get => "oa:TextQuoteSelector"; + set => throw new System.NotImplementedException(); + } - [JsonProperty(Order = 51, PropertyName = "exact")] - public string? Exact { get; set; } + [JsonProperty(Order = 51, PropertyName = "exact")] + public string? Exact { get; set; } - [JsonProperty(Order = 52, PropertyName = "prefix")] - public string? Prefix { get; set; } + [JsonProperty(Order = 52, PropertyName = "prefix")] + public string? Prefix { get; set; } - [JsonProperty(Order = 53, PropertyName = "suffix")] - public string? Suffix { get; set; } - } -} + [JsonProperty(Order = 53, PropertyName = "suffix")] + public string? Suffix { get; set; } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/Search/V2/AutoCompleteService.cs b/src/IIIF/IIIF/Search/V2/AutoCompleteService.cs index 842f3ec..e1a1d70 100644 --- a/src/IIIF/IIIF/Search/V2/AutoCompleteService.cs +++ b/src/IIIF/IIIF/Search/V2/AutoCompleteService.cs @@ -1,15 +1,14 @@ using IIIF.Presentation.V3.Content; -namespace IIIF.Search.V2 +namespace IIIF.Search.V2; + +public class AutoCompleteService : ExternalResource, IService { - public class AutoCompleteService : ExternalResource, IService + public const string AutoComplete2Profile = "http://iiif.io/api/search/2/autocomplete"; + + public AutoCompleteService() : base(nameof(AutoCompleteService)) { - public const string AutoComplete2Profile = "http://iiif.io/api/search/2/autocomplete"; - - public AutoCompleteService() : base(nameof(AutoCompleteService)) - { - // Allow callers to decide whether to set the @context - Profile = AutoComplete2Profile; - } + // Allow callers to decide whether to set the @context + Profile = AutoComplete2Profile; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Search/V2/SearchService.cs b/src/IIIF/IIIF/Search/V2/SearchService.cs index 660902c..cb9855b 100644 --- a/src/IIIF/IIIF/Search/V2/SearchService.cs +++ b/src/IIIF/IIIF/Search/V2/SearchService.cs @@ -1,16 +1,15 @@ using IIIF.Presentation.V3.Content; -namespace IIIF.Search.V2 +namespace IIIF.Search.V2; + +public class SearchService : ExternalResource, IService { - public class SearchService : ExternalResource, IService + public const string Search2Context = "http://iiif.io/api/search/2/context.json"; + public const string Search2Profile = "http://iiif.io/api/search/2/search"; + + public SearchService() : base(nameof(SearchService)) { - public const string Search2Context = "http://iiif.io/api/search/2/context.json"; - public const string Search2Profile = "http://iiif.io/api/search/2/search"; - - public SearchService() : base(nameof(SearchService)) - { - // Allow callers to decide whether to set the @context - Profile = Search2Profile; - } + // Allow callers to decide whether to set the @context + Profile = Search2Profile; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/CamelCaseEnumAttribute.cs b/src/IIIF/IIIF/Serialisation/CamelCaseEnumAttribute.cs index 003a733..7a9cc85 100644 --- a/src/IIIF/IIIF/Serialisation/CamelCaseEnumAttribute.cs +++ b/src/IIIF/IIIF/Serialisation/CamelCaseEnumAttribute.cs @@ -1,22 +1,21 @@ using System; -namespace IIIF.Serialisation +namespace IIIF.Serialisation; + +/// +/// Any enum property decorated with this attribute will have a serialised value of it's string representation +/// in camelCase (e.g. InvalidRequest => invalidRequest) +/// +[AttributeUsage(AttributeTargets.Property)] +public class CamelCaseEnumAttribute : Attribute +{ +} + +/// +/// Any enum property decorated with this attribute will have a serialised value of it's string representation +/// in camelCase (e.g. InvalidRequest => invalidRequest) +/// +[AttributeUsage(AttributeTargets.Property)] +public class EnumAsStringAttribute : Attribute { - /// - /// Any enum property decorated with this attribute will have a serialised value of it's string representation - /// in camelCase (e.g. InvalidRequest => invalidRequest) - /// - [AttributeUsage(AttributeTargets.Property)] - public class CamelCaseEnumAttribute : Attribute - { - } - - /// - /// Any enum property decorated with this attribute will have a serialised value of it's string representation - /// in camelCase (e.g. InvalidRequest => invalidRequest) - /// - [AttributeUsage(AttributeTargets.Property)] - public class EnumAsStringAttribute : Attribute - { - } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/Deserialisation/AnnotationV3Converter.cs b/src/IIIF/IIIF/Serialisation/Deserialisation/AnnotationV3Converter.cs index 5427b0c..1348a8a 100644 --- a/src/IIIF/IIIF/Serialisation/Deserialisation/AnnotationV3Converter.cs +++ b/src/IIIF/IIIF/Serialisation/Deserialisation/AnnotationV3Converter.cs @@ -3,29 +3,28 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace IIIF.Serialisation.Deserialisation +namespace IIIF.Serialisation.Deserialisation; + +/// +/// JsonConverter to handle reading objects to concrete type. +/// +public class AnnotationV3Converter : ReadOnlyConverter { - /// - /// JsonConverter to handle reading objects to concrete type. - /// - public class AnnotationV3Converter : ReadOnlyConverter + public override IAnnotation? ReadJson(JsonReader reader, Type objectType, IAnnotation? existingValue, + bool hasExistingValue, + JsonSerializer serializer) { - public override IAnnotation? ReadJson(JsonReader reader, Type objectType, IAnnotation? existingValue, - bool hasExistingValue, - JsonSerializer serializer) - { - var jsonObject = JObject.Load(reader); + var jsonObject = JObject.Load(reader); - IAnnotation annotation = jsonObject["motivation"].Value() switch - { - IIIF.Presentation.V3.Constants.Motivation.Painting => new PaintingAnnotation(), - IIIF.Presentation.V3.Constants.Motivation.Supplementing => new SupplementingDocumentAnnotation(), - IIIF.Presentation.V3.Constants.Motivation.Classifying => new TypeClassifyingAnnotation(), - _ => new Annotation() - }; + IAnnotation annotation = jsonObject["motivation"].Value() switch + { + Presentation.V3.Constants.Motivation.Painting => new PaintingAnnotation(), + Presentation.V3.Constants.Motivation.Supplementing => new SupplementingDocumentAnnotation(), + Presentation.V3.Constants.Motivation.Classifying => new TypeClassifyingAnnotation(), + _ => new Annotation() + }; - serializer.Populate(jsonObject.CreateReader(), annotation); - return annotation; - } + serializer.Populate(jsonObject.CreateReader(), annotation); + return annotation; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/Deserialisation/ExternalResourceConverter.cs b/src/IIIF/IIIF/Serialisation/Deserialisation/ExternalResourceConverter.cs index 54b30da..dba7e87 100644 --- a/src/IIIF/IIIF/Serialisation/Deserialisation/ExternalResourceConverter.cs +++ b/src/IIIF/IIIF/Serialisation/Deserialisation/ExternalResourceConverter.cs @@ -3,29 +3,28 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace IIIF.Serialisation.Deserialisation +namespace IIIF.Serialisation.Deserialisation; + +/// +/// JsonConverter to handle reading objects to concrete type. +/// +public class ExternalResourceConverter : ReadOnlyConverter { - /// - /// JsonConverter to handle reading objects to concrete type. - /// - public class ExternalResourceConverter : ReadOnlyConverter + public override ExternalResource? ReadJson(JsonReader reader, Type objectType, ExternalResource? existingValue, + bool hasExistingValue, JsonSerializer serializer) { - public override ExternalResource? ReadJson(JsonReader reader, Type objectType, ExternalResource? existingValue, - bool hasExistingValue, JsonSerializer serializer) - { - var jsonObject = JObject.Load(reader); + var jsonObject = JObject.Load(reader); - var type = jsonObject["type"].Value(); - ExternalResource externalResource = type switch - { - nameof(Audio) => new Audio(), - nameof(Video) => new Video(), - nameof(Image) => new Image(), - _ => new ExternalResource(type) - }; + var type = jsonObject["type"].Value(); + var externalResource = type switch + { + nameof(Audio) => new Audio(), + nameof(Video) => new Video(), + nameof(Image) => new Image(), + _ => new ExternalResource(type) + }; - serializer.Populate(jsonObject.CreateReader(), externalResource); - return externalResource; - } + serializer.Populate(jsonObject.CreateReader(), externalResource); + return externalResource; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/Deserialisation/PaintableConverter.cs b/src/IIIF/IIIF/Serialisation/Deserialisation/PaintableConverter.cs index fa94e13..fb30cfc 100644 --- a/src/IIIF/IIIF/Serialisation/Deserialisation/PaintableConverter.cs +++ b/src/IIIF/IIIF/Serialisation/Deserialisation/PaintableConverter.cs @@ -5,30 +5,29 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace IIIF.Serialisation.Deserialisation +namespace IIIF.Serialisation.Deserialisation; + +/// +/// JsonConverter to handle reading objects to concrete type. +/// +public class PaintableConverter : ReadOnlyConverter { - /// - /// JsonConverter to handle reading objects to concrete type. - /// - public class PaintableConverter : ReadOnlyConverter + public override IPaintable? ReadJson(JsonReader reader, Type objectType, IPaintable? existingValue, + bool hasExistingValue, JsonSerializer serializer) { - public override IPaintable? ReadJson(JsonReader reader, Type objectType, IPaintable? existingValue, - bool hasExistingValue, JsonSerializer serializer) - { - var jsonObject = JObject.Load(reader); + var jsonObject = JObject.Load(reader); - IPaintable? paintable = jsonObject["type"].Value() switch - { - nameof(Audio) => new Audio(), - nameof(Video) => new Video(), - nameof(Image) => new Image(), - nameof(Canvas) => new Canvas(), - "Choice" => new PaintingChoice(), - _ => null - }; + IPaintable? paintable = jsonObject["type"].Value() switch + { + nameof(Audio) => new Audio(), + nameof(Video) => new Video(), + nameof(Image) => new Image(), + nameof(Canvas) => new Canvas(), + "Choice" => new PaintingChoice(), + _ => null + }; - serializer.Populate(jsonObject.CreateReader(), paintable); - return paintable; - } + serializer.Populate(jsonObject.CreateReader(), paintable); + return paintable; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/Deserialisation/ReadOnlyConverter.cs b/src/IIIF/IIIF/Serialisation/Deserialisation/ReadOnlyConverter.cs index 7b75c86..bc6194e 100644 --- a/src/IIIF/IIIF/Serialisation/Deserialisation/ReadOnlyConverter.cs +++ b/src/IIIF/IIIF/Serialisation/Deserialisation/ReadOnlyConverter.cs @@ -1,14 +1,14 @@ using System; using Newtonsoft.Json; -namespace IIIF.Serialisation.Deserialisation +namespace IIIF.Serialisation.Deserialisation; + +public abstract class ReadOnlyConverter : JsonConverter { - public abstract class ReadOnlyConverter : JsonConverter + public override bool CanWrite => false; + + public override void WriteJson(JsonWriter writer, T? value, JsonSerializer serializer) { - public override bool CanWrite => false; - public override void WriteJson(JsonWriter writer, T? value, JsonSerializer serializer) - { - throw new NotImplementedException(); - } + throw new NotImplementedException(); } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/Deserialisation/ResourceBaseV3Converter.cs b/src/IIIF/IIIF/Serialisation/Deserialisation/ResourceBaseV3Converter.cs index cff12d8..512c891 100644 --- a/src/IIIF/IIIF/Serialisation/Deserialisation/ResourceBaseV3Converter.cs +++ b/src/IIIF/IIIF/Serialisation/Deserialisation/ResourceBaseV3Converter.cs @@ -6,72 +6,69 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace IIIF.Serialisation.Deserialisation +namespace IIIF.Serialisation.Deserialisation; + +/// +/// JsonConverter to handle reading objects to concrete type. +/// Falls through to if type cannot be identified. +/// +public class ResourceBaseV3Converter : ReadOnlyConverter { - /// - /// JsonConverter to handle reading objects to concrete type. - /// Falls through to if type cannot be identified. - /// - public class ResourceBaseV3Converter : ReadOnlyConverter + public override ResourceBase? ReadJson(JsonReader reader, Type objectType, ResourceBase? existingValue, + bool hasExistingValue, + JsonSerializer serializer) { - public override ResourceBase? ReadJson(JsonReader reader, Type objectType, ResourceBase? existingValue, bool hasExistingValue, - JsonSerializer serializer) - { - var jsonObject = JObject.Load(reader); + var jsonObject = JObject.Load(reader); - var resourceBase = IdentifyConcreteType(jsonObject); + var resourceBase = IdentifyConcreteType(jsonObject); + + serializer.Populate(jsonObject.CreateReader(), resourceBase); + return resourceBase; + } - serializer.Populate(jsonObject.CreateReader(), resourceBase); + private static ResourceBase? IdentifyConcreteType(JObject jsonObject) + { + ResourceBase? resourceBase = null; + if (!jsonObject.ContainsKey("type")) + { + resourceBase = new ClassifyingBody(jsonObject["id"].Value()); return resourceBase; } - private static ResourceBase? IdentifyConcreteType(JObject jsonObject) + var type = jsonObject["type"].Value(); + resourceBase = type switch { - ResourceBase? resourceBase = null; - if (!jsonObject.ContainsKey("type")) - { - resourceBase = new ClassifyingBody(jsonObject["id"].Value()); - return resourceBase; - } - - var type = jsonObject["type"].Value(); - resourceBase = type switch - { - nameof(ImageService3) => new ImageService3(), - nameof(Agent) => new Agent(), - nameof(Annotation) => new Annotation(), - nameof(AnnotationCollection) => new AnnotationCollection(), - nameof(AnnotationPage) => new AnnotationPage(), - nameof(Audio) => new Audio(), - nameof(Canvas) => new Canvas(), - nameof(Collection) => new Collection(), - nameof(Image) => new Image(), - nameof(Manifest) => new Manifest(), - nameof(SpecificResource) => new SpecificResource(), - nameof(TextualBody) => new TextualBody(jsonObject["value"].Value()), - _ => null - }; + nameof(ImageService3) => new ImageService3(), + nameof(Agent) => new Agent(), + nameof(Annotation) => new Annotation(), + nameof(AnnotationCollection) => new AnnotationCollection(), + nameof(AnnotationPage) => new AnnotationPage(), + nameof(Audio) => new Audio(), + nameof(Canvas) => new Canvas(), + nameof(Collection) => new Collection(), + nameof(Image) => new Image(), + nameof(Manifest) => new Manifest(), + nameof(SpecificResource) => new SpecificResource(), + nameof(TextualBody) => new TextualBody(jsonObject["value"].Value()), + _ => null + }; - if (resourceBase != null) return resourceBase; + if (resourceBase != null) return resourceBase; - if (jsonObject.ContainsKey("motivation")) + if (jsonObject.ContainsKey("motivation")) + { + var motivation = jsonObject["motivation"].Value(); + resourceBase = motivation switch { - var motivation = jsonObject["motivation"].Value(); - resourceBase = motivation switch - { - IIIF.Presentation.V3.Constants.Motivation.Supplementing => new SupplementingDocumentAnnotation(), - IIIF.Presentation.V3.Constants.Motivation.Painting => new PaintingAnnotation(), - IIIF.Presentation.V3.Constants.Motivation.Classifying => new TypeClassifyingAnnotation(), - _ => new UnknownMotivation(motivation) - }; - } + Presentation.V3.Constants.Motivation.Supplementing => new SupplementingDocumentAnnotation(), + Presentation.V3.Constants.Motivation.Painting => new PaintingAnnotation(), + Presentation.V3.Constants.Motivation.Classifying => new TypeClassifyingAnnotation(), + _ => new UnknownMotivation(motivation) + }; + } - if (resourceBase == null) - { - return new ExternalResource(type); - } + if (resourceBase == null) return new ExternalResource(type); - return resourceBase; - } + return resourceBase; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/Deserialisation/SelectorConverter.cs b/src/IIIF/IIIF/Serialisation/Deserialisation/SelectorConverter.cs index b3c3c9d..e141bd0 100644 --- a/src/IIIF/IIIF/Serialisation/Deserialisation/SelectorConverter.cs +++ b/src/IIIF/IIIF/Serialisation/Deserialisation/SelectorConverter.cs @@ -3,28 +3,27 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace IIIF.Serialisation.Deserialisation +namespace IIIF.Serialisation.Deserialisation; + +/// +/// JsonConverter to handle reading objects to concrete type. +/// +public class SelectorConverter : ReadOnlyConverter { - /// - /// JsonConverter to handle reading objects to concrete type. - /// - public class SelectorConverter : ReadOnlyConverter + public override ISelector? ReadJson(JsonReader reader, Type objectType, ISelector? existingValue, + bool hasExistingValue, JsonSerializer serializer) { - public override ISelector? ReadJson(JsonReader reader, Type objectType, ISelector? existingValue, - bool hasExistingValue, JsonSerializer serializer) - { - var jsonObject = JObject.Load(reader); + var jsonObject = JObject.Load(reader); - ISelector selector = jsonObject["type"].Value() switch - { - nameof(AudioContentSelector) => new AudioContentSelector(), - nameof(ImageApiSelector) => new ImageApiSelector(), - nameof(PointSelector) => new PointSelector(), - nameof(VideoContentSelector) => new VideoContentSelector(), - }; + ISelector selector = jsonObject["type"].Value() switch + { + nameof(AudioContentSelector) => new AudioContentSelector(), + nameof(ImageApiSelector) => new ImageApiSelector(), + nameof(PointSelector) => new PointSelector(), + nameof(VideoContentSelector) => new VideoContentSelector() + }; - serializer.Populate(jsonObject.CreateReader(), selector); - return selector; - } + serializer.Populate(jsonObject.CreateReader(), selector); + return selector; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/Deserialisation/ServiceConverter.cs b/src/IIIF/IIIF/Serialisation/Deserialisation/ServiceConverter.cs index 6ca4766..aecae6d 100644 --- a/src/IIIF/IIIF/Serialisation/Deserialisation/ServiceConverter.cs +++ b/src/IIIF/IIIF/Serialisation/Deserialisation/ServiceConverter.cs @@ -4,93 +4,80 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace IIIF.Serialisation.Deserialisation +namespace IIIF.Serialisation.Deserialisation; + +/// +/// JsonConverter to handle reading objects to concrete type. +/// +public class ServiceConverter : ReadOnlyConverter { - /// - /// JsonConverter to handle reading objects to concrete type. - /// - public class ServiceConverter : ReadOnlyConverter + public override IService? ReadJson(JsonReader reader, Type objectType, IService? existingValue, + bool hasExistingValue, JsonSerializer serializer) { - public override IService? ReadJson(JsonReader reader, Type objectType, IService? existingValue, - bool hasExistingValue, JsonSerializer serializer) - { - var jsonObject = JObject.Load(reader); + var jsonObject = JObject.Load(reader); - var service = IdentifyConcreteType(jsonObject); - - serializer.Populate(jsonObject.CreateReader(), service); - return service; - } + var service = IdentifyConcreteType(jsonObject); - private static IService? IdentifyConcreteType(JObject jsonObject) - { - IService? service = null; - var atType = jsonObject["@type"]; - if (atType != null) + serializer.Populate(jsonObject.CreateReader(), service); + return service; + } + + private static IService? IdentifyConcreteType(JObject jsonObject) + { + IService? service = null; + var atType = jsonObject["@type"]; + if (atType != null) + service = atType.Value() switch { - service = atType.Value() switch + "SearchService1" => new Search.V1.SearchService(), + "AuthLogoutService1" => new Auth.V1.AuthLogoutService(), + "AuthTokenService1" => new Auth.V1.AuthTokenService(), + "AutoCompleteService1" => new Search.V1.AutoCompleteService(), + nameof(ImageService2) => new ImageService2(), + _ => null + }; + + if (service == null) + { + var type = jsonObject["type"]; + if (type != null) + service = type.Value() switch { - "SearchService1" => new Search.V1.SearchService(), - "AuthLogoutService1" => new Auth.V1.AuthLogoutService(), - "AuthTokenService1" => new Auth.V1.AuthTokenService(), - "AutoCompleteService1" => new Search.V1.AutoCompleteService(), - nameof(ImageService2) => new ImageService2(), + nameof(ImageService3) => new ImageService3(), _ => null }; - } - - if (service == null) + } + + if (service == null) + { + var profile = jsonObject["profile"].Value(); + service = profile switch { - var type = jsonObject["type"]; - if (type != null) - { - service = type.Value() switch - { - nameof(ImageService3) => new ImageService3(), - _ => null - }; - } - } + Auth.V1.AuthLogoutService.AuthLogout1Profile => new Auth.V1.AuthLogoutService(), + Auth.V1.AuthTokenService.AuthToken1Profile => new Auth.V1.AuthTokenService(), + Auth.V0.AuthLogoutService.AuthLogout0Profile => new Auth.V0.AuthLogoutService(), + Auth.V0.AuthTokenService.AuthToken0Profile => new Auth.V0.AuthTokenService(), + Search.V2.AutoCompleteService.AutoComplete2Profile => new Search.V2.AutoCompleteService(), + Search.V1.AutoCompleteService.AutoCompleteService1Profile => new Search.V1.AutoCompleteService(), + Search.V2.SearchService.Search2Profile => new Search.V2.SearchService(), + _ => null + }; if (service == null) { - var profile = jsonObject["profile"].Value(); - service = profile switch - { - Auth.V1.AuthLogoutService.AuthLogout1Profile => new Auth.V1.AuthLogoutService(), - Auth.V1.AuthTokenService.AuthToken1Profile => new Auth.V1.AuthTokenService(), - Auth.V0.AuthLogoutService.AuthLogout0Profile => new Auth.V0.AuthLogoutService(), - Auth.V0.AuthTokenService.AuthToken0Profile => new Auth.V0.AuthTokenService(), - Search.V2.AutoCompleteService.AutoComplete2Profile => new Search.V2.AutoCompleteService(), - Search.V1.AutoCompleteService.AutoCompleteService1Profile => new Search.V1.AutoCompleteService(), - Search.V2.SearchService.Search2Profile => new Search.V2.SearchService(), - _ => null - }; + const string auth0 = "http://iiif.io/api/auth/0/"; + const string auth1 = "http://iiif.io/api/auth/1/"; - if (service == null) - { - const string auth0 = "http://iiif.io/api/auth/0/"; - const string auth1 = "http://iiif.io/api/auth/1/"; - - if (profile.StartsWith(auth0)) - { - service = new Auth.V0.AuthCookieService(profile); - } - else if (profile.StartsWith(auth1)) - { - service =new Auth.V1.AuthCookieService(profile); - } - } + if (profile.StartsWith(auth0)) + service = new Auth.V0.AuthCookieService(profile); + else if (profile.StartsWith(auth1)) service = new Auth.V1.AuthCookieService(profile); } + } - // TODO handle ResourceBase items + // TODO handle ResourceBase items - if (service == null) - { - service = new V2ServiceReference(); - } + if (service == null) service = new V2ServiceReference(); - return service; - } + return service; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/Deserialisation/StructuralLocationConverter.cs b/src/IIIF/IIIF/Serialisation/Deserialisation/StructuralLocationConverter.cs index e17a458..8d88b3e 100644 --- a/src/IIIF/IIIF/Serialisation/Deserialisation/StructuralLocationConverter.cs +++ b/src/IIIF/IIIF/Serialisation/Deserialisation/StructuralLocationConverter.cs @@ -4,27 +4,26 @@ using Newtonsoft.Json.Linq; using Range = IIIF.Presentation.V3.Range; -namespace IIIF.Serialisation.Deserialisation +namespace IIIF.Serialisation.Deserialisation; + +/// +/// JsonConverter to handle reading objects to concrete type. +/// +public class StructuralLocationConverter : ReadOnlyConverter { - /// - /// JsonConverter to handle reading objects to concrete type. - /// - public class StructuralLocationConverter : ReadOnlyConverter + public override IStructuralLocation? ReadJson(JsonReader reader, Type objectType, + IStructuralLocation? existingValue, bool hasExistingValue, JsonSerializer serializer) { - public override IStructuralLocation? ReadJson(JsonReader reader, Type objectType, - IStructuralLocation? existingValue, bool hasExistingValue, JsonSerializer serializer) + var jsonObject = JObject.Load(reader); + + IStructuralLocation structuralLocation = jsonObject["type"].Value() switch { - var jsonObject = JObject.Load(reader); - - IStructuralLocation structuralLocation = jsonObject["type"].Value() switch - { - nameof(Canvas) => new Canvas(), - nameof(Range) => new Range(), - nameof(SpecificResource) => new SpecificResource(), - }; - - serializer.Populate(jsonObject.CreateReader(), structuralLocation); - return structuralLocation; - } + nameof(Canvas) => new Canvas(), + nameof(Range) => new Range(), + nameof(SpecificResource) => new SpecificResource() + }; + + serializer.Populate(jsonObject.CreateReader(), structuralLocation); + return structuralLocation; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/EnumCamelCaseValueConverter.cs b/src/IIIF/IIIF/Serialisation/EnumCamelCaseValueConverter.cs index 79f81c3..ee14aa5 100644 --- a/src/IIIF/IIIF/Serialisation/EnumCamelCaseValueConverter.cs +++ b/src/IIIF/IIIF/Serialisation/EnumCamelCaseValueConverter.cs @@ -2,62 +2,50 @@ using IIIF.Presentation.V2.Serialisation; using Newtonsoft.Json; -namespace IIIF.Serialisation +namespace IIIF.Serialisation; + +/// +/// Serialises enum as camelCase representation of enum value +/// +public class EnumCamelCaseValueConverter : WriteOnlyConverter { - /// - /// Serialises enum as camelCase representation of enum value - /// - public class EnumCamelCaseValueConverter : WriteOnlyConverter + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - if (value is not Enum enumValue) - { - throw new ArgumentException( - $"EnumCamelCaseValueConverter expected enum but got {value.GetType().Name}", nameof(value)); - } + if (value is not Enum enumValue) + throw new ArgumentException( + $"EnumCamelCaseValueConverter expected enum but got {value.GetType().Name}", nameof(value)); - writer.WriteValue(ConvertToCamelCase(enumValue.ToString())); - } - - private string ConvertToCamelCase(string name) - { - // Based on System.Text.Json.JsonNamingPolicy.CamelCase.ConvertName implementation - if (string.IsNullOrEmpty(name) || !char.IsUpper(name[0])) - { - return name; - } + writer.WriteValue(ConvertToCamelCase(enumValue.ToString())); + } - char[] chars = name.ToCharArray(); - FixCasing(chars); - return new string(chars); - } + private string ConvertToCamelCase(string name) + { + // Based on System.Text.Json.JsonNamingPolicy.CamelCase.ConvertName implementation + if (string.IsNullOrEmpty(name) || !char.IsUpper(name[0])) return name; - private static void FixCasing(Span chars) + var chars = name.ToCharArray(); + FixCasing(chars); + return new string(chars); + } + + private static void FixCasing(Span chars) + { + for (var i = 0; i < chars.Length; i++) { - for (int i = 0; i < chars.Length; i++) + if (i == 1 && !char.IsUpper(chars[i])) break; + + var hasNext = i + 1 < chars.Length; + + // Stop when next char is already lowercase. + if (i > 0 && hasNext && !char.IsUpper(chars[i + 1])) { - if (i == 1 && !char.IsUpper(chars[i])) - { - break; - } - - bool hasNext = (i + 1 < chars.Length); - - // Stop when next char is already lowercase. - if (i > 0 && hasNext && !char.IsUpper(chars[i + 1])) - { - // If the next char is a space, lowercase current char before exiting. - if (chars[i + 1] == ' ') - { - chars[i] = char.ToLowerInvariant(chars[i]); - } - - break; - } - - chars[i] = char.ToLowerInvariant(chars[i]); + // If the next char is a space, lowercase current char before exiting. + if (chars[i + 1] == ' ') chars[i] = char.ToLowerInvariant(chars[i]); + + break; } + + chars[i] = char.ToLowerInvariant(chars[i]); } } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/EnumStringValueConverter.cs b/src/IIIF/IIIF/Serialisation/EnumStringValueConverter.cs index 0be1659..8847df4 100644 --- a/src/IIIF/IIIF/Serialisation/EnumStringValueConverter.cs +++ b/src/IIIF/IIIF/Serialisation/EnumStringValueConverter.cs @@ -1,22 +1,19 @@ using System; using Newtonsoft.Json; -namespace IIIF.Serialisation +namespace IIIF.Serialisation; + +/// +/// Serialises enum as camelCase representation of enum value +/// +public class EnumStringValueConverter : WriteOnlyConverter { - /// - /// Serialises enum as camelCase representation of enum value - /// - public class EnumStringValueConverter : WriteOnlyConverter + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - if (value is not Enum enumValue) - { - throw new ArgumentException( - $"EnumCamelCaseValueConverter expected enum but got {value.GetType().Name}", nameof(value)); - } + if (value is not Enum enumValue) + throw new ArgumentException( + $"EnumCamelCaseValueConverter expected enum but got {value.GetType().Name}", nameof(value)); - writer.WriteValue(enumValue.ToString()); - } + writer.WriteValue(enumValue.ToString()); } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/EnumX.cs b/src/IIIF/IIIF/Serialisation/EnumX.cs index 75eaf05..7608350 100644 --- a/src/IIIF/IIIF/Serialisation/EnumX.cs +++ b/src/IIIF/IIIF/Serialisation/EnumX.cs @@ -2,22 +2,21 @@ using System.Linq; using System.Reflection; -namespace IIIF.Serialisation +namespace IIIF.Serialisation; + +internal static class EnumX { - internal static class EnumX + /// + /// Get Description value for enum. Will use if found, or fall back to value.ToString(). + /// + /// Value to get description for. + /// Type of enum. + /// String description for enum value. + public static string GetDescription(this T enumValue) + where T : System.Enum { - /// - /// Get Description value for enum. Will use if found, or fall back to value.ToString(). - /// - /// Value to get description for. - /// Type of enum. - /// String description for enum value. - public static string GetDescription(this T enumValue) - where T : System.Enum - { - var memberInfo = typeof(T).GetMember(enumValue.ToString()).Single(); - var desc = memberInfo.GetCustomAttribute(); - return desc == null ? enumValue.ToString() : desc.Description; - } + var memberInfo = typeof(T).GetMember(enumValue.ToString()).Single(); + var desc = memberInfo.GetCustomAttribute(); + return desc == null ? enumValue.ToString() : desc.Description; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/IIIFSerialiserX.cs b/src/IIIF/IIIF/Serialisation/IIIFSerialiserX.cs index 211a25d..70562c6 100644 --- a/src/IIIF/IIIF/Serialisation/IIIFSerialiserX.cs +++ b/src/IIIF/IIIF/Serialisation/IIIFSerialiserX.cs @@ -3,85 +3,88 @@ using IIIF.Serialisation.Deserialisation; using Newtonsoft.Json; -namespace IIIF.Serialisation +namespace IIIF.Serialisation; + +/// +/// Extension methods to aid with serialisation and deserialisation +/// +public static class IIIFSerialiserX { - /// - /// Extension methods to aid with serialisation and deserialisation - /// - public static class IIIFSerialiserX + private static readonly JsonSerializerSettings SerializerSettings = new() { - private static readonly JsonSerializerSettings SerializerSettings = new() + NullValueHandling = NullValueHandling.Ignore, + ContractResolver = new PrettyIIIFContractResolver(), + Formatting = Formatting.Indented, + ReferenceLoopHandling = ReferenceLoopHandling.Serialize, + Converters = new List { - NullValueHandling = NullValueHandling.Ignore, - ContractResolver = new PrettyIIIFContractResolver(), - Formatting = Formatting.Indented, - ReferenceLoopHandling = ReferenceLoopHandling.Serialize, - Converters = new List - { - new ImageService2Converter(), new SizeConverter(), new StringArrayConverter(), - new ServiceReferenceConverter(), new ThumbnailConverter() - } - }; + new ImageService2Converter(), new SizeConverter(), new StringArrayConverter(), + new ServiceReferenceConverter(), new ThumbnailConverter() + } + }; - private static readonly JsonSerializerSettings DeserializerSettings = new() + private static readonly JsonSerializerSettings DeserializerSettings = new() + { + NullValueHandling = NullValueHandling.Ignore, + ContractResolver = new PrettyIIIFContractResolver(), + Formatting = Formatting.Indented, + Converters = new List { - NullValueHandling = NullValueHandling.Ignore, - ContractResolver = new PrettyIIIFContractResolver(), - Formatting = Formatting.Indented, - Converters = new List - { - new ImageService2Converter(), new AnnotationV3Converter(), new ResourceBaseV3Converter(), - new StructuralLocationConverter(), new ExternalResourceConverter(), new PaintableConverter(), - new SelectorConverter(), new ServiceConverter() - } - }; + new ImageService2Converter(), new AnnotationV3Converter(), new ResourceBaseV3Converter(), + new StructuralLocationConverter(), new ExternalResourceConverter(), new PaintableConverter(), + new SelectorConverter(), new ServiceConverter() + } + }; - /// - /// Serialise specified iiif resource to json string. - /// - /// IIIF resource to serialise. - /// JSON string representation of iiif resource. - public static string AsJson(this JsonLdBase iiifResource) - => JsonConvert.SerializeObject(iiifResource, SerializerSettings); + /// + /// Serialise specified iiif resource to json string. + /// + /// IIIF resource to serialise. + /// JSON string representation of iiif resource. + public static string AsJson(this JsonLdBase iiifResource) + { + return JsonConvert.SerializeObject(iiifResource, SerializerSettings); + } - /// - /// Serialise specified iiif resource to stream. - /// - /// IIIF resource to serialise. - /// Stream to serialise object to - /// Stream representation of iiif resource json. - public static void AsJsonStream(this JsonLdBase iiifResource, Stream stream) - { - using var sw = new StreamWriter(stream, leaveOpen: true); - using var writer = new JsonTextWriter(sw); - var serializer = JsonSerializer.Create(SerializerSettings); - serializer.Serialize(writer, iiifResource); - writer.Flush(); - } + /// + /// Serialise specified iiif resource to stream. + /// + /// IIIF resource to serialise. + /// Stream to serialise object to + /// Stream representation of iiif resource json. + public static void AsJsonStream(this JsonLdBase iiifResource, Stream stream) + { + using var sw = new StreamWriter(stream, leaveOpen: true); + using var writer = new JsonTextWriter(sw); + var serializer = JsonSerializer.Create(SerializerSettings); + serializer.Serialize(writer, iiifResource); + writer.Flush(); + } - /// - /// Deserialize specified iiif resource from json string. - /// - /// IIIF resource to deserialize. - /// Type of object to deserialize to. - /// - public static TTarget FromJson(this string iiifResource) - where TTarget : JsonLdBase - => JsonConvert.DeserializeObject(iiifResource, DeserializerSettings); - - /// - /// Deserialize specified iiif resource from stream containing json - /// - /// IIIF resource to deserialize. - /// Type of object to deserialize to. - /// - public static TTarget FromJsonStream(this Stream iiifResource) - where TTarget : JsonLdBase - { - using var sr = new StreamReader(iiifResource); - using var reader = new JsonTextReader(sr); - var serializer = JsonSerializer.Create(DeserializerSettings); - return serializer.Deserialize(reader); - } + /// + /// Deserialize specified iiif resource from json string. + /// + /// IIIF resource to deserialize. + /// Type of object to deserialize to. + /// + public static TTarget FromJson(this string iiifResource) + where TTarget : JsonLdBase + { + return JsonConvert.DeserializeObject(iiifResource, DeserializerSettings); + } + + /// + /// Deserialize specified iiif resource from stream containing json + /// + /// IIIF resource to deserialize. + /// Type of object to deserialize to. + /// + public static TTarget FromJsonStream(this Stream iiifResource) + where TTarget : JsonLdBase + { + using var sr = new StreamReader(iiifResource); + using var reader = new JsonTextReader(sr); + var serializer = JsonSerializer.Create(DeserializerSettings); + return serializer.Deserialize(reader); } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/ImageService2Converter.cs b/src/IIIF/IIIF/Serialisation/ImageService2Converter.cs index 1a6c2f3..f006ecd 100644 --- a/src/IIIF/IIIF/Serialisation/ImageService2Converter.cs +++ b/src/IIIF/IIIF/Serialisation/ImageService2Converter.cs @@ -3,93 +3,86 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace IIIF.Serialisation +namespace IIIF.Serialisation; + +/// +/// Converter for object, serialises Profile and ProfileDescription into single +/// "profile" property. +/// +public class ImageService2Converter : JsonConverter { - /// - /// Converter for object, serialises Profile and ProfileDescription into single - /// "profile" property. - /// - public class ImageService2Converter : JsonConverter - { - private const string ProfileProperty = "profile"; + private const string ProfileProperty = "profile"; - public override void WriteJson(JsonWriter writer, ImageService2? value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, ImageService2? value, JsonSerializer serializer) + { + // Create a copy to avoid circular reference issue bombing out + var customSerializer = serializer.CreateCopy(converter => converter is not ImageService2Converter); + if (value?.ProfileDescription == null) { - // Create a copy to avoid circular reference issue bombing out - var customSerializer = serializer.CreateCopy(converter => converter is not ImageService2Converter); - if (value?.ProfileDescription == null) - { - // If ProfileDescription is null then no special handling required - customSerializer.Serialize(writer, value); - return; - } - - var imageService = JObject.FromObject(value, customSerializer); - imageService[ProfileProperty] = GetProfileElement(value, customSerializer); - imageService.WriteTo(writer); + // If ProfileDescription is null then no special handling required + customSerializer.Serialize(writer, value); + return; } - public override ImageService2 ReadJson(JsonReader reader, Type objectType, ImageService2? existingValue, bool hasExistingValue, - JsonSerializer serializer) - { - var jsonObject = JObject.Load(reader); - existingValue ??= new ImageService2(); + var imageService = JObject.FromObject(value, customSerializer); + imageService[ProfileProperty] = GetProfileElement(value, customSerializer); + imageService.WriteTo(writer); + } - DeserialiseProperty(existingValue, jsonObject); + public override ImageService2 ReadJson(JsonReader reader, Type objectType, ImageService2? existingValue, + bool hasExistingValue, + JsonSerializer serializer) + { + var jsonObject = JObject.Load(reader); + existingValue ??= new ImageService2(); - serializer.Populate(jsonObject.CreateReader(), existingValue); - return existingValue; - } + DeserialiseProperty(existingValue, jsonObject); + + serializer.Populate(jsonObject.CreateReader(), existingValue); + return existingValue; + } + + private static void DeserialiseProperty(ImageService2 imageService, JObject jsonObject) + { + var profileToken = jsonObject.GetValue(ProfileProperty); - private static void DeserialiseProperty(ImageService2 imageService, JObject jsonObject) + // If "profile" property null or a string then default serialisation will cope + if (profileToken == null || profileToken.Type == JTokenType.String) return; + + void SetProfileDescription(JObject profileDescription) { - var profileToken = jsonObject.GetValue(ProfileProperty); - - // If "profile" property null or a string then default serialisation will cope - if (profileToken == null || profileToken.Type == JTokenType.String) return; - - void SetProfileDescription(JObject profileDescription) - { - imageService.ProfileDescription = profileDescription.ToObject(); - } - - bool handled = false; - if (profileToken is JArray profileArray) - { - // if "profile" is an array then populate profile + profileDescription properties - foreach (var profileValue in profileArray) - { - if (profileValue.Type == JTokenType.String) - { - imageService.Profile = profileValue.ToString(); - } - else if (profileValue is JObject profileDescription) - { - SetProfileDescription(profileDescription); - } - } - - handled = true; - } - else if (profileToken is JObject profileDescription) - { - // else if "profile" is a JObject, populate ProfileDescription property only - SetProfileDescription(profileDescription); - handled = true; - } - - if (handled) jsonObject.Remove(ProfileProperty); + imageService.ProfileDescription = profileDescription.ToObject(); } - private static JToken GetProfileElement(ImageService2? value, JsonSerializer serializer) + var handled = false; + if (profileToken is JArray profileArray) { - var profileDescription = JToken.FromObject(value.ProfileDescription, serializer); - - var newProfile = string.IsNullOrEmpty(value.Profile) - ? new JProperty("property", profileDescription) - : new JProperty("property", value.Profile, profileDescription); + // if "profile" is an array then populate profile + profileDescription properties + foreach (var profileValue in profileArray) + if (profileValue.Type == JTokenType.String) + imageService.Profile = profileValue.ToString(); + else if (profileValue is JObject profileDescription) SetProfileDescription(profileDescription); - return newProfile.Value; + handled = true; } + else if (profileToken is JObject profileDescription) + { + // else if "profile" is a JObject, populate ProfileDescription property only + SetProfileDescription(profileDescription); + handled = true; + } + + if (handled) jsonObject.Remove(ProfileProperty); + } + + private static JToken GetProfileElement(ImageService2? value, JsonSerializer serializer) + { + var profileDescription = JToken.FromObject(value.ProfileDescription, serializer); + + var newProfile = string.IsNullOrEmpty(value.Profile) + ? new JProperty("property", profileDescription) + : new JProperty("property", value.Profile, profileDescription); + + return newProfile.Value; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/JsonSerializerExtensions.cs b/src/IIIF/IIIF/Serialisation/JsonSerializerExtensions.cs index 945ee96..7b74af0 100644 --- a/src/IIIF/IIIF/Serialisation/JsonSerializerExtensions.cs +++ b/src/IIIF/IIIF/Serialisation/JsonSerializerExtensions.cs @@ -2,55 +2,52 @@ using System.Linq; using Newtonsoft.Json; -namespace IIIF.Serialisation +namespace IIIF.Serialisation; + +internal static class JsonSerializerExtensions { - internal static class JsonSerializerExtensions + /// + /// Create a copy of , filtering Converters array + /// + /// JsonSerializer to copy. + /// Predicate to filter JsonConverters - if true then converter copied + /// New JsonSerializer instance + /// Based on https://stackoverflow.com/a/38230327/83096 + public static JsonSerializer CreateCopy(this JsonSerializer serializer, Func converterFilter) { - /// - /// Create a copy of , filtering Converters array - /// - /// JsonSerializer to copy. - /// Predicate to filter JsonConverters - if true then converter copied - /// New JsonSerializer instance - /// Based on https://stackoverflow.com/a/38230327/83096 - public static JsonSerializer CreateCopy(this JsonSerializer serializer, Func converterFilter) + var copiedSerializer = new JsonSerializer { - var copiedSerializer = new JsonSerializer - { - Context = serializer.Context, - Culture = serializer.Culture, - ContractResolver = serializer.ContractResolver, - ConstructorHandling = serializer.ConstructorHandling, - CheckAdditionalContent = serializer.CheckAdditionalContent, - DateFormatHandling = serializer.DateFormatHandling, - DateFormatString = serializer.DateFormatString, - DateParseHandling = serializer.DateParseHandling, - DateTimeZoneHandling = serializer.DateTimeZoneHandling, - DefaultValueHandling = serializer.DefaultValueHandling, - EqualityComparer = serializer.EqualityComparer, - FloatFormatHandling = serializer.FloatFormatHandling, - Formatting = serializer.Formatting, - FloatParseHandling = serializer.FloatParseHandling, - MaxDepth = serializer.MaxDepth, - MetadataPropertyHandling = serializer.MetadataPropertyHandling, - MissingMemberHandling = serializer.MissingMemberHandling, - NullValueHandling = serializer.NullValueHandling, - ObjectCreationHandling = serializer.ObjectCreationHandling, - PreserveReferencesHandling = serializer.PreserveReferencesHandling, - ReferenceResolver = serializer.ReferenceResolver, - ReferenceLoopHandling = serializer.ReferenceLoopHandling, - StringEscapeHandling = serializer.StringEscapeHandling, - TraceWriter = serializer.TraceWriter, - TypeNameHandling = serializer.TypeNameHandling, - SerializationBinder = serializer.SerializationBinder, - TypeNameAssemblyFormatHandling = serializer.TypeNameAssemblyFormatHandling - }; - - foreach (var converter in serializer.Converters.Where(c => converterFilter(c))) - { - copiedSerializer.Converters.Add(converter); - } - return copiedSerializer; - } + Context = serializer.Context, + Culture = serializer.Culture, + ContractResolver = serializer.ContractResolver, + ConstructorHandling = serializer.ConstructorHandling, + CheckAdditionalContent = serializer.CheckAdditionalContent, + DateFormatHandling = serializer.DateFormatHandling, + DateFormatString = serializer.DateFormatString, + DateParseHandling = serializer.DateParseHandling, + DateTimeZoneHandling = serializer.DateTimeZoneHandling, + DefaultValueHandling = serializer.DefaultValueHandling, + EqualityComparer = serializer.EqualityComparer, + FloatFormatHandling = serializer.FloatFormatHandling, + Formatting = serializer.Formatting, + FloatParseHandling = serializer.FloatParseHandling, + MaxDepth = serializer.MaxDepth, + MetadataPropertyHandling = serializer.MetadataPropertyHandling, + MissingMemberHandling = serializer.MissingMemberHandling, + NullValueHandling = serializer.NullValueHandling, + ObjectCreationHandling = serializer.ObjectCreationHandling, + PreserveReferencesHandling = serializer.PreserveReferencesHandling, + ReferenceResolver = serializer.ReferenceResolver, + ReferenceLoopHandling = serializer.ReferenceLoopHandling, + StringEscapeHandling = serializer.StringEscapeHandling, + TraceWriter = serializer.TraceWriter, + TypeNameHandling = serializer.TypeNameHandling, + SerializationBinder = serializer.SerializationBinder, + TypeNameAssemblyFormatHandling = serializer.TypeNameAssemblyFormatHandling + }; + + foreach (var converter in serializer.Converters.Where(c => converterFilter(c))) + copiedSerializer.Converters.Add(converter); + return copiedSerializer; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/LanguageMapSerialiser.cs b/src/IIIF/IIIF/Serialisation/LanguageMapSerialiser.cs index 424d541..2af58f5 100644 --- a/src/IIIF/IIIF/Serialisation/LanguageMapSerialiser.cs +++ b/src/IIIF/IIIF/Serialisation/LanguageMapSerialiser.cs @@ -5,51 +5,47 @@ using IIIF.Presentation.V3.Strings; using Newtonsoft.Json; -namespace IIIF.Serialisation +namespace IIIF.Serialisation; + +/// +/// Serialises as json, putting values on single line if single language with single value +/// of less than 40 chars. +/// +public class LanguageMapSerialiser : WriteOnlyConverter { - /// - /// Serialises as json, putting values on single line if single language with single value - /// of less than 40 chars. - /// - public class LanguageMapSerialiser : WriteOnlyConverter + public override void WriteJson(JsonWriter writer, LanguageMap? value, JsonSerializer serializer) { - public override void WriteJson(JsonWriter writer, LanguageMap? value, JsonSerializer serializer) - { - if (value == null) - { - throw new ArgumentException( - "LanguageMapSerialiser cannot serialise a null object", nameof(value)); - } + if (value == null) + throw new ArgumentException( + "LanguageMapSerialiser cannot serialise a null object", nameof(value)); - if (value.Count == 0) - { - throw new ArgumentException( - $"LanguageMapSerialiser cannot serialise an empty array {value.GetType().Name}", nameof(value)); - } - - // if has a single language, with a single value, of length less than X, output without formatting - if (value.Count == 1) + if (value.Count == 0) + throw new ArgumentException( + $"LanguageMapSerialiser cannot serialise an empty array {value.GetType().Name}", nameof(value)); + + // if has a single language, with a single value, of length less than X, output without formatting + if (value.Count == 1) + { + var (key, list) = value.Single(); + if (list.Count == 1 && list[0].Length <= StringArrayConverter.MaxChars) { - var (key, list) = value.Single(); - if (list.Count == 1 && list[0].Length <= StringArrayConverter.MaxChars) + var output = new Dictionary> { - var output = new Dictionary> - { - [key] = list - }; - writer.WriteRawValue(JsonConvert.SerializeObject(output, Formatting.None)); - return; - } + [key] = list + }; + writer.WriteRawValue(JsonConvert.SerializeObject(output, Formatting.None)); + return; } + } - // output with 'normal' formatting. Can't just call serialiser with value or we end up in an infinite loop - writer.WriteStartObject(); - foreach (var (key, values) in value) - { - writer.WritePropertyName(key); - serializer.Serialize(writer, values); - } - writer.WriteEndObject(); + // output with 'normal' formatting. Can't just call serialiser with value or we end up in an infinite loop + writer.WriteStartObject(); + foreach (var (key, values) in value) + { + writer.WritePropertyName(key); + serializer.Serialize(writer, values); } + + writer.WriteEndObject(); } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/ObjectIfSingleAttribute.cs b/src/IIIF/IIIF/Serialisation/ObjectIfSingleAttribute.cs index 457c4ff..b5f17cc 100644 --- a/src/IIIF/IIIF/Serialisation/ObjectIfSingleAttribute.cs +++ b/src/IIIF/IIIF/Serialisation/ObjectIfSingleAttribute.cs @@ -1,13 +1,12 @@ using System; -namespace IIIF.Serialisation +namespace IIIF.Serialisation; + +/// +/// Signifies that an object should be serialized as a single object if .Count == 1. +/// Else, serialize as an array. +/// +[AttributeUsage(AttributeTargets.Property)] +public class ObjectIfSingleAttribute : Attribute { - /// - /// Signifies that an object should be serialized as a single object if .Count == 1. - /// Else, serialize as an array. - /// - [AttributeUsage(AttributeTargets.Property)] - public class ObjectIfSingleAttribute : Attribute - { - } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/ObjectIfSingleConverter.cs b/src/IIIF/IIIF/Serialisation/ObjectIfSingleConverter.cs index dddacf7..5fe8bd9 100644 --- a/src/IIIF/IIIF/Serialisation/ObjectIfSingleConverter.cs +++ b/src/IIIF/IIIF/Serialisation/ObjectIfSingleConverter.cs @@ -3,87 +3,75 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace IIIF.Serialisation +namespace IIIF.Serialisation; + +/// +/// Serialises List as a single object if 1 element, else renders json array. +/// Note - for deserialisation to work the targettype must be List{T} +/// +public class ObjectIfSingleConverter : JsonConverter { - /// - /// Serialises List as a single object if 1 element, else renders json array. - /// Note - for deserialisation to work the targettype must be List{T} - /// - public class ObjectIfSingleConverter : JsonConverter + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - if (value is not IList list) - { - throw new ArgumentException( - $"ObjectIfSingleConverter expected IEnumerable but got {value.GetType().Name}", nameof(value)); - } - - if (list.Count > 1) - { - writer.WriteStartArray(); - } - - foreach (var element in list) - { - serializer.Serialize(writer, element); - } - - if (list.Count > 1) - { - writer.WriteEndArray(); - } - } + if (value is not IList list) + throw new ArgumentException( + $"ObjectIfSingleConverter expected IEnumerable but got {value.GetType().Name}", nameof(value)); - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) - { - if (reader.TokenType == JsonToken.Null) return null; - - // [ {"foo": "bar"} ] or {"foo": "bar"} - if (reader.TokenType is JsonToken.StartArray or JsonToken.StartObject) - { - var isArray = reader.TokenType is JsonToken.StartArray; - return Deserialise(reader, objectType, serializer, isArray); - } - - // "foo bar" - target type will be List - if (reader.TokenType == JsonToken.String) - { - return CreateListOfOne(objectType, reader.Value?.ToString()); - } - - throw new FormatException("Unable to convert provided object"); - } - - public override bool CanConvert(Type objectType) - => objectType.IsAssignableTo(typeof(IEnumerable)); + if (list.Count > 1) writer.WriteStartArray(); + + foreach (var element in list) serializer.Serialize(writer, element); - private object? Deserialise(JsonReader reader, Type objectType, JsonSerializer serializer, bool isArray) + if (list.Count > 1) writer.WriteEndArray(); + } + + public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, + JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + + // [ {"foo": "bar"} ] or {"foo": "bar"} + if (reader.TokenType is JsonToken.StartArray or JsonToken.StartObject) { - var targetType = Activator.CreateInstance(objectType); - if (targetType == null) return null; - - // Remove current type to avoid circular loop - var jsonSerializer = serializer.CreateCopy(converter => converter is not ObjectIfSingleConverter); - - // If this is already an array load it as-is, else load the JObject into an array - var array = isArray ? JArray.Load(reader) : new JArray(JObject.Load(reader)); - jsonSerializer.Populate(array.CreateReader(), targetType); - return targetType; + var isArray = reader.TokenType is JsonToken.StartArray; + return Deserialise(reader, objectType, serializer, isArray); } - private object? CreateListOfOne(Type objectType, object? singleValue) - { - if (singleValue == null) return null; - - var targetType = Activator.CreateInstance(objectType); - if (targetType == null) return null; + // "foo bar" - target type will be List + if (reader.TokenType == JsonToken.String) return CreateListOfOne(objectType, reader.Value?.ToString()); - var addMethod = objectType.GetMethod("Add"); - if (addMethod == null) return null; + throw new FormatException("Unable to convert provided object"); + } - addMethod.Invoke(targetType, new[] { singleValue }); - return targetType; - } + public override bool CanConvert(Type objectType) + { + return objectType.IsAssignableTo(typeof(IEnumerable)); + } + + private object? Deserialise(JsonReader reader, Type objectType, JsonSerializer serializer, bool isArray) + { + var targetType = Activator.CreateInstance(objectType); + if (targetType == null) return null; + + // Remove current type to avoid circular loop + var jsonSerializer = serializer.CreateCopy(converter => converter is not ObjectIfSingleConverter); + + // If this is already an array load it as-is, else load the JObject into an array + var array = isArray ? JArray.Load(reader) : new JArray(JObject.Load(reader)); + jsonSerializer.Populate(array.CreateReader(), targetType); + return targetType; + } + + private object? CreateListOfOne(Type objectType, object? singleValue) + { + if (singleValue == null) return null; + + var targetType = Activator.CreateInstance(objectType); + if (targetType == null) return null; + + var addMethod = objectType.GetMethod("Add"); + if (addMethod == null) return null; + + addMethod.Invoke(targetType, new[] { singleValue }); + return targetType; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/PrettyIIIFContractResolver.cs b/src/IIIF/IIIF/Serialisation/PrettyIIIFContractResolver.cs index 4120dd9..e68b2fd 100644 --- a/src/IIIF/IIIF/Serialisation/PrettyIIIFContractResolver.cs +++ b/src/IIIF/IIIF/Serialisation/PrettyIIIFContractResolver.cs @@ -4,74 +4,63 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; -namespace IIIF.Serialisation +namespace IIIF.Serialisation; + +/// +/// Resolves mappings for IIIF objects. +/// +public class PrettyIIIFContractResolver : CamelCasePropertyNamesContractResolver { - /// - /// Resolves mappings for IIIF objects. - /// - public class PrettyIIIFContractResolver : CamelCasePropertyNamesContractResolver + // adapted from https://stackoverflow.com/a/34903827 + private static readonly ObjectIfSingleConverter ObjectIfSingleConverter = new(); + + private static readonly EnumCamelCaseValueConverter EnumCamelCaseValueConverter = new(); + private static readonly EnumStringValueConverter EnumStringValueConverter = new(); + + protected override JsonProperty CreateProperty( + MemberInfo member, + MemberSerialization memberSerialization) { - // adapted from https://stackoverflow.com/a/34903827 - private static readonly ObjectIfSingleConverter ObjectIfSingleConverter = new(); + var property = base.CreateProperty(member, memberSerialization); + var pType = property.PropertyType; + if (pType == null) return property; + + // Don't serialise Width or Height if they have a zero value + if (member.Name is "Width" or "Height") + property.ShouldSerialize = instance => + { + var o = instance + .GetType() + .GetProperty(member.Name) + ?.GetValue(instance); + return o != null && (int)o != 0; + }; - private static readonly EnumCamelCaseValueConverter EnumCamelCaseValueConverter = new(); - private static readonly EnumStringValueConverter EnumStringValueConverter = new(); + if (member.GetCustomAttribute() != null) + property.Converter = EnumCamelCaseValueConverter; - protected override JsonProperty CreateProperty( - MemberInfo member, - MemberSerialization memberSerialization) + if (member.GetCustomAttribute() != null) property.Converter = EnumStringValueConverter; + + // Don't serialise empty lists, unless they have the [RequiredOutput] attribute + if (pType.IsGenericType && pType.GetGenericTypeDefinition() == typeof(List<>)) { - var property = base.CreateProperty(member, memberSerialization); - var pType = property.PropertyType; - if (pType == null) return property; - - // Don't serialise Width or Height if they have a zero value - if (member.Name is "Width" or "Height") + property.ShouldSerialize = instance => { - property.ShouldSerialize = instance => - { - var o = instance + IList? list = null; + if (member.MemberType == MemberTypes.Property) + list = instance .GetType() .GetProperty(member.Name) - ?.GetValue(instance); - return o != null && (int) o != 0; - }; - } - - if (member.GetCustomAttribute() != null) - { - property.Converter = EnumCamelCaseValueConverter; - } - - if (member.GetCustomAttribute() != null) - { - property.Converter = EnumStringValueConverter; - } - - // Don't serialise empty lists, unless they have the [RequiredOutput] attribute - if (pType.IsGenericType && pType.GetGenericTypeDefinition() == typeof(List<>)) - { - property.ShouldSerialize = instance => - { - IList? list = null; - if (member.MemberType == MemberTypes.Property) - { - list = instance - .GetType() - .GetProperty(member.Name) - ?.GetValue(instance, null) as IList; - } - var hasContent = list != null && list.Count > 0; - var requiredOutputAttr = member.GetCustomAttribute(); - return hasContent || requiredOutputAttr != null; - }; + ?.GetValue(instance, null) as IList; + var hasContent = list != null && list.Count > 0; + var requiredOutputAttr = member.GetCustomAttribute(); + return hasContent || requiredOutputAttr != null; + }; - if (member.GetCustomAttribute() != null) - { - property.Converter = ObjectIfSingleConverter; - } - } - return property; + if (member.GetCustomAttribute() != null) + property.Converter = ObjectIfSingleConverter; } + + return property; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/RequiredOutputAttribute.cs b/src/IIIF/IIIF/Serialisation/RequiredOutputAttribute.cs index 57050a7..0799aa4 100644 --- a/src/IIIF/IIIF/Serialisation/RequiredOutputAttribute.cs +++ b/src/IIIF/IIIF/Serialisation/RequiredOutputAttribute.cs @@ -1,13 +1,12 @@ using System; -namespace IIIF.Serialisation +namespace IIIF.Serialisation; + +/// +/// Any list property decorated with this attribute will be serialized to json as '[]' if empty. +/// Default behaviour is to not output empty lists. +/// +[AttributeUsage(AttributeTargets.Property)] +public class RequiredOutputAttribute : Attribute { - /// - /// Any list property decorated with this attribute will be serialized to json as '[]' if empty. - /// Default behaviour is to not output empty lists. - /// - [AttributeUsage(AttributeTargets.Property)] - public class RequiredOutputAttribute : Attribute - { - } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/ServiceReferenceConverter.cs b/src/IIIF/IIIF/Serialisation/ServiceReferenceConverter.cs index f03bda4..a1cd3ed 100644 --- a/src/IIIF/IIIF/Serialisation/ServiceReferenceConverter.cs +++ b/src/IIIF/IIIF/Serialisation/ServiceReferenceConverter.cs @@ -1,22 +1,17 @@ using Newtonsoft.Json; -namespace IIIF.Serialisation +namespace IIIF.Serialisation; + +/// +/// Converter for , will output type and id if type present, else just id as string +/// +public class ServiceReferenceConverter : WriteOnlyConverter { - /// - /// Converter for , will output type and id if type present, else just id as string - /// - public class ServiceReferenceConverter : WriteOnlyConverter + public override void WriteJson(JsonWriter writer, V2ServiceReference? value, JsonSerializer serializer) { - public override void WriteJson(JsonWriter writer, V2ServiceReference? value, JsonSerializer serializer) - { - if (string.IsNullOrEmpty(value?.Type) && !string.IsNullOrEmpty(value?.Id)) - { - writer.WriteValue(value!.Id); - } - else - { - writer.WriteRawValue(JsonConvert.SerializeObject(value)); - } - } + if (string.IsNullOrEmpty(value?.Type) && !string.IsNullOrEmpty(value?.Id)) + writer.WriteValue(value!.Id); + else + writer.WriteRawValue(JsonConvert.SerializeObject(value)); } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/SizeConverter.cs b/src/IIIF/IIIF/Serialisation/SizeConverter.cs index fcda0c1..3573c5d 100644 --- a/src/IIIF/IIIF/Serialisation/SizeConverter.cs +++ b/src/IIIF/IIIF/Serialisation/SizeConverter.cs @@ -1,14 +1,15 @@ using Newtonsoft.Json; -namespace IIIF.Serialisation +namespace IIIF.Serialisation; + +/// +/// Converter for type that writes single line +/// e.g. {"width":100,"height":200} +/// +public class SizeConverter : WriteOnlyConverter { - /// - /// Converter for type that writes single line - /// e.g. {"width":100,"height":200} - /// - public class SizeConverter : WriteOnlyConverter + public override void WriteJson(JsonWriter writer, Size? value, JsonSerializer serializer) { - public override void WriteJson(JsonWriter writer, Size? value, JsonSerializer serializer) - => writer.WriteRawValue(JsonConvert.SerializeObject(value, Formatting.None)); + writer.WriteRawValue(JsonConvert.SerializeObject(value, Formatting.None)); } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/StringArrayConverter.cs b/src/IIIF/IIIF/Serialisation/StringArrayConverter.cs index 43bb4a1..71d228a 100644 --- a/src/IIIF/IIIF/Serialisation/StringArrayConverter.cs +++ b/src/IIIF/IIIF/Serialisation/StringArrayConverter.cs @@ -3,34 +3,30 @@ using IIIF.Presentation.V2.Serialisation; using Newtonsoft.Json; -namespace IIIF.Serialisation +namespace IIIF.Serialisation; + +/// +/// Converter for string arrays, will output on one line of single value and less than 50 chars. +/// +public class StringArrayConverter : WriteOnlyConverter> { - /// - /// Converter for string arrays, will output on one line of single value and less than 50 chars. - /// - public class StringArrayConverter : WriteOnlyConverter> + // Arrays of single strings below this length will be output on 1 line + public const int MaxChars = 40; + + public override void WriteJson(JsonWriter writer, IEnumerable? value, JsonSerializer serializer) { - // Arrays of single strings below this length will be output on 1 line - public const int MaxChars = 40; - - public override void WriteJson(JsonWriter writer, IEnumerable? value, JsonSerializer serializer) - { - var stringList = value?.ToList(); - if (stringList == null || stringList.Count == 0) return; + var stringList = value?.ToList(); + if (stringList == null || stringList.Count == 0) return; - if (stringList.Count == 1 && stringList[0].Length <= MaxChars) - { - writer.WriteRawValue(JsonConvert.SerializeObject(value, Formatting.None)); - } - else - { - writer.WriteStartArray(); - foreach (var val in stringList) - { - writer.WriteValue(val); - } - writer.WriteEndArray(); - } + if (stringList.Count == 1 && stringList[0].Length <= MaxChars) + { + writer.WriteRawValue(JsonConvert.SerializeObject(value, Formatting.None)); + } + else + { + writer.WriteStartArray(); + foreach (var val in stringList) writer.WriteValue(val); + writer.WriteEndArray(); } } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/TargetConverter.cs b/src/IIIF/IIIF/Serialisation/TargetConverter.cs index 067431d..c9c60e9 100644 --- a/src/IIIF/IIIF/Serialisation/TargetConverter.cs +++ b/src/IIIF/IIIF/Serialisation/TargetConverter.cs @@ -5,51 +5,52 @@ using Newtonsoft.Json.Linq; using Range = IIIF.Presentation.V3.Range; -namespace IIIF.Serialisation +namespace IIIF.Serialisation; + +/// +/// for objects. +/// Serialises Id-only objects to string representation and deserialises back. +/// +public class TargetConverter : JsonConverter { - /// - /// for objects. - /// Serialises Id-only objects to string representation and deserialises back. - /// - public class TargetConverter : JsonConverter + public override IStructuralLocation? ReadJson(JsonReader reader, Type objectType, + IStructuralLocation? existingValue, + bool hasExistingValue, JsonSerializer serializer) { - public override IStructuralLocation? ReadJson(JsonReader reader, Type objectType, - IStructuralLocation? existingValue, - bool hasExistingValue, JsonSerializer serializer) + if (reader.TokenType == JsonToken.String) { - if (reader.TokenType == JsonToken.String) - { - return new Canvas { Id = reader.Value.ToString() }; - } - else if (reader.TokenType == JsonToken.StartObject) - { - var obj = JObject.Load(reader); - - var type = obj["type"].Value(); - return type switch - { - nameof(Canvas) => obj.ToObject(), - nameof(Range) => obj.ToObject(), - nameof(SpecificResource) => obj.ToObject() - }; - } - - return null; + return new Canvas { Id = reader.Value.ToString() }; } - - public override void WriteJson(JsonWriter writer, IStructuralLocation? value, JsonSerializer serializer) + else if (reader.TokenType == JsonToken.StartObject) { - if (value is Canvas canvas && (canvas.SerialiseTargetAsId || IsSimpleCanvas(canvas))) + var obj = JObject.Load(reader); + + var type = obj["type"].Value(); + return type switch { - writer.WriteValue(canvas.Id); - return; - } + nameof(Canvas) => obj.ToObject(), + nameof(Range) => obj.ToObject(), + nameof(SpecificResource) => obj.ToObject() + }; + } - // Default, pass through behaviour: - JObject.FromObject(value, serializer).WriteTo(writer); + return null; + } + + public override void WriteJson(JsonWriter writer, IStructuralLocation? value, JsonSerializer serializer) + { + if (value is Canvas canvas && (canvas.SerialiseTargetAsId || IsSimpleCanvas(canvas))) + { + writer.WriteValue(canvas.Id); + return; } - private static bool IsSimpleCanvas(Canvas canvas) - => canvas.Width == null && canvas.Duration == null && canvas.Items.IsNullOrEmpty(); + // Default, pass through behaviour: + JObject.FromObject(value, serializer).WriteTo(writer); + } + + private static bool IsSimpleCanvas(Canvas canvas) + { + return canvas.Width == null && canvas.Duration == null && canvas.Items.IsNullOrEmpty(); } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/ThumbnailConverter.cs b/src/IIIF/IIIF/Serialisation/ThumbnailConverter.cs index 6cf35c2..2753a60 100644 --- a/src/IIIF/IIIF/Serialisation/ThumbnailConverter.cs +++ b/src/IIIF/IIIF/Serialisation/ThumbnailConverter.cs @@ -3,36 +3,31 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; -namespace IIIF.Serialisation +namespace IIIF.Serialisation; + +/// +/// Converter for , will just Id as string if only Id populated +/// +public class ThumbnailConverter : WriteOnlyConverter { - /// - /// Converter for , will just Id as string if only Id populated - /// - public class ThumbnailConverter : WriteOnlyConverter + public override void WriteJson(JsonWriter writer, Thumbnail? value, JsonSerializer serializer) { - public override void WriteJson(JsonWriter writer, Thumbnail? value, JsonSerializer serializer) - { - // If we have Id and nothing else, write Id as string - if (!string.IsNullOrEmpty(value?.Id) && - string.IsNullOrEmpty(value.Type) && - value.Service == null && - value.Context == null && - value.Description == null && - value.Label == null && - value.Profile == null) - { - writer.WriteValue(value!.Id); - } - else - { - writer.WriteRawValue(JsonConvert.SerializeObject(value, - new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - ContractResolver = new PrettyIIIFContractResolver(), - Formatting = Formatting.Indented, - })); - } - } + // If we have Id and nothing else, write Id as string + if (!string.IsNullOrEmpty(value?.Id) && + string.IsNullOrEmpty(value.Type) && + value.Service == null && + value.Context == null && + value.Description == null && + value.Label == null && + value.Profile == null) + writer.WriteValue(value!.Id); + else + writer.WriteRawValue(JsonConvert.SerializeObject(value, + new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore, + ContractResolver = new PrettyIIIFContractResolver(), + Formatting = Formatting.Indented + })); } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/WriteOnlyConverter.cs b/src/IIIF/IIIF/Serialisation/WriteOnlyConverter.cs index 491fcb1..8cb4d65 100644 --- a/src/IIIF/IIIF/Serialisation/WriteOnlyConverter.cs +++ b/src/IIIF/IIIF/Serialisation/WriteOnlyConverter.cs @@ -1,26 +1,31 @@ using System; using Newtonsoft.Json; -namespace IIIF.Serialisation +namespace IIIF.Serialisation; + +public abstract class WriteOnlyConverter : JsonConverter { - public abstract class WriteOnlyConverter : JsonConverter + public override bool CanConvert(Type objectType) { - public override bool CanConvert(Type objectType) => true; - public override bool CanRead => false; + return true; + } + + public override bool CanRead => false; - public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, - JsonSerializer serializer) - { - throw new NotImplementedException(); - } + public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, + JsonSerializer serializer) + { + throw new NotImplementedException(); } +} + +public abstract class WriteOnlyConverter : JsonConverter +{ + public override bool CanRead => false; - public abstract class WriteOnlyConverter : JsonConverter + public override T ReadJson(JsonReader reader, Type objectType, T? existingValue, bool hasExistingValue, + JsonSerializer serializer) { - public override bool CanRead => false; - public override T ReadJson(JsonReader reader, Type objectType, T? existingValue, bool hasExistingValue, JsonSerializer serializer) - { - throw new NotImplementedException(); - } + throw new NotImplementedException(); } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/XsdDateTimeConverter.cs b/src/IIIF/IIIF/Serialisation/XsdDateTimeConverter.cs index bc2c783..d4bb48c 100644 --- a/src/IIIF/IIIF/Serialisation/XsdDateTimeConverter.cs +++ b/src/IIIF/IIIF/Serialisation/XsdDateTimeConverter.cs @@ -1,19 +1,18 @@ using System; using Newtonsoft.Json; -namespace IIIF.Serialisation +namespace IIIF.Serialisation; + +/// +/// Outputs DateTime as a valid xsd:dateTime format, see https://www.w3.org/TR/xmlschema11-2/#dateTime +/// +public class XsdDateTimeConverter : WriteOnlyConverter { - /// - /// Outputs DateTime as a valid xsd:dateTime format, see https://www.w3.org/TR/xmlschema11-2/#dateTime - /// - public class XsdDateTimeConverter : WriteOnlyConverter + public override void WriteJson(JsonWriter writer, DateTime? value, JsonSerializer serializer) { - public override void WriteJson(JsonWriter writer, DateTime? value, JsonSerializer serializer) - { - if (value == null) return; + if (value == null) return; - var xsdDate = value.Value.ToString("yyyy-MM-ddTHH:mm:ssK"); - writer.WriteValue(xsdDate); - } + var xsdDate = value.Value.ToString("yyyy-MM-ddTHH:mm:ssK"); + writer.WriteValue(xsdDate); } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Size.cs b/src/IIIF/IIIF/Size.cs index e41cea3..48d9d94 100644 --- a/src/IIIF/IIIF/Size.cs +++ b/src/IIIF/IIIF/Size.cs @@ -1,187 +1,193 @@ using System; -using Newtonsoft.Json; -namespace IIIF +namespace IIIF; + +/// +/// Represents the 2d size of an object. +/// +public class Size { + [JsonProperty(PropertyName = "width")] public int Width { get; private set; } + + [JsonProperty(PropertyName = "height")] + public int Height { get; private set; } + + [JsonIgnore] public int MaxDimension => Width > Height ? Width : Height; + + public override string ToString() + { + return $"{Width},{Height}"; + } + + /// + /// Create new Size object with specified width and height. + /// + public Size(int width, int height) + { + Width = width; + Height = height; + } + + /// + /// Get size object as w,h array + /// + /// + public int[] ToArray() + { + return new[] { Width, Height }; + } + + /// + /// Checks if current Size is confined within specified size. + /// + /// Size object to check if confined within. + /// true if current item would fit inside specified size; else false. + public bool IsConfinedWithin(Size confineSize) + { + return Width <= confineSize.Width && Height <= confineSize.Height; + } + + /// + /// Create new Size object representing square. + /// + /// width and height of square + public static Size Square(int dimension) + { + return new(dimension, dimension); + } + + /// + /// Create new Size object from "w,h" array. + /// + /// w,h array + /// New Size object + public static Size FromArray(int[] size) + { + return new(size[0], size[1]); + } + + /// + /// Create new Size object from "w,h" string. + /// + /// String representing size. + /// New Size object + public static Size FromString(string size) + { + var parts = size.Split(","); + return new Size(int.Parse(parts[0]), int.Parse(parts[1])); + } + + /// + /// Confine specified Size object to bounding square of specified size. + /// + /// Dimension of bounding square to confine object to. + /// Size object to Confine dimensions to. + /// New object with dimensions bound to specified square. + public static Size Confine(int boundingSquare, Size imageSize) + { + return Confine(Square(boundingSquare), imageSize); + } + + /// + /// Confine specified Size object to bounding square of specified size. + /// This is similar to FitWithin() but will returned original size if already within confines. + /// + /// Dimension of bounding square to confine object to. + /// Size object to Confine dimensions to. + /// New object with dimensions bound to specified square. + public static Size Confine(Size requiredSize, Size imageSize) + { + if (imageSize.Width <= requiredSize.Width && imageSize.Height <= requiredSize.Height) return imageSize; + + return FitWithin(requiredSize, imageSize); + } + + /// + /// Fit specified Size object withing bounding square of specified size, allowing to grow if required. + /// + /// Dimension of bounding square to confine object to. + /// Size object to Confine dimensions to. + /// New object with dimensions bound to specified square. + public static Size FitWithin(Size requiredSize, Size imageSize) + { + var scaleW = requiredSize.Width / (double)imageSize.Width; + var scaleH = requiredSize.Height / (double)imageSize.Height; + var scale = Math.Min(scaleW, scaleH); + return new Size( + (int)Math.Round(imageSize.Width * scale), + (int)Math.Round(imageSize.Height * scale) + ); + } + /// - /// Represents the 2d size of an object. + /// Resize specified Size to new Width and/or Height. + /// Maintains aspect ratio unless both are specified. /// - public class Size + public static Size Resize(Size size, int? targetWidth = null, int? targetHeight = null) { - [JsonProperty(PropertyName = "width")] public int Width { get; private set; } - - [JsonProperty(PropertyName = "height")] - public int Height { get; private set; } - - [JsonIgnore] public int MaxDimension => Width > Height ? Width : Height; - - public override string ToString() => $"{Width},{Height}"; - - /// - /// Create new Size object with specified width and height. - /// - public Size(int width, int height) - { - Width = width; - Height = height; - } - - /// - /// Get size object as w,h array - /// - /// - public int[] ToArray() => new[] { Width, Height }; - - /// - /// Checks if current Size is confined within specified size. - /// - /// Size object to check if confined within. - /// true if current item would fit inside specified size; else false. - public bool IsConfinedWithin(Size confineSize) - => Width <= confineSize.Width && Height <= confineSize.Height; - - /// - /// Create new Size object representing square. - /// - /// width and height of square - public static Size Square(int dimension) - => new(dimension, dimension); - - /// - /// Create new Size object from "w,h" array. - /// - /// w,h array - /// New Size object - public static Size FromArray(int[] size) - => new(size[0], size[1]); - - /// - /// Create new Size object from "w,h" string. - /// - /// String representing size. - /// New Size object - public static Size FromString(string size) - { - var parts = size.Split(","); - return new Size(int.Parse(parts[0]), int.Parse(parts[1])); - } - - /// - /// Confine specified Size object to bounding square of specified size. - /// - /// Dimension of bounding square to confine object to. - /// Size object to Confine dimensions to. - /// New object with dimensions bound to specified square. - public static Size Confine(int boundingSquare, Size imageSize) - => Confine(Size.Square(boundingSquare), imageSize); - - /// - /// Confine specified Size object to bounding square of specified size. - /// This is similar to FitWithin() but will returned original size if already within confines. - /// - /// Dimension of bounding square to confine object to. - /// Size object to Confine dimensions to. - /// New object with dimensions bound to specified square. - public static Size Confine(Size requiredSize, Size imageSize) - { - if (imageSize.Width <= requiredSize.Width && imageSize.Height <= requiredSize.Height) - { - return imageSize; - } - - return FitWithin(requiredSize, imageSize); - } - - /// - /// Fit specified Size object withing bounding square of specified size, allowing to grow if required. - /// - /// Dimension of bounding square to confine object to. - /// Size object to Confine dimensions to. - /// New object with dimensions bound to specified square. - public static Size FitWithin(Size requiredSize, Size imageSize) - { - var scaleW = requiredSize.Width / (double)imageSize.Width; - var scaleH = requiredSize.Height / (double)imageSize.Height; - var scale = Math.Min(scaleW, scaleH); - return new Size( - (int)Math.Round(imageSize.Width * scale), - (int)Math.Round(imageSize.Height * scale) - ); - } - - /// - /// Resize specified Size to new Width and/or Height. - /// Maintains aspect ratio unless both are specified. - /// - public static Size Resize(Size size, int? targetWidth = null, int? targetHeight = null) - { - if (!targetWidth.HasValue && !targetHeight.HasValue) - throw new InvalidOperationException("Cannot confine size without dimensions"); - - if (targetWidth.HasValue && targetHeight.HasValue) - { - return new Size(targetWidth.Value, targetHeight.Value); - } - - if (size.GetShape() == ImageShape.Square) - return Square(targetWidth ?? targetHeight ?? -1); - - return new Size( - targetWidth ?? size.Width * targetHeight!.Value / size.Height, - targetHeight ?? size.Height * targetWidth!.Value / size.Width); - } - - /// - /// Resize specified Size growing/shrinking by specified % value - /// - public static Size ResizePercent(Size size, float percentage) - { - var width = Convert.ToInt32(size.Width * (percentage / 100)); - var height = Convert.ToInt32(size.Height * (percentage / 100)); - return new Size(width, height); - } - - /// - /// Get % size difference between larger and smaller size, based on longest edge. - /// - public static double GetSizeIncreasePercent(Size largerSize, Size smallerSize) - { - var largeMax = largerSize.MaxDimension; - var smallMax = smallerSize.MaxDimension; - if (smallMax > largeMax) throw new InvalidOperationException("Larger size must be larger than smaller"); - - return ((largeMax / (double)smallMax) - 1) * 100; - } - - /// - /// Get the shape of image based on it's dimensions. - /// - public ImageShape GetShape() - { - if (Width == Height) return ImageShape.Square; - - return Width > Height ? ImageShape.Landscape : ImageShape.Portrait; - } + if (!targetWidth.HasValue && !targetHeight.HasValue) + throw new InvalidOperationException("Cannot confine size without dimensions"); + + if (targetWidth.HasValue && targetHeight.HasValue) return new Size(targetWidth.Value, targetHeight.Value); + + if (size.GetShape() == ImageShape.Square) + return Square(targetWidth ?? targetHeight ?? -1); + + return new Size( + targetWidth ?? size.Width * targetHeight!.Value / size.Height, + targetHeight ?? size.Height * targetWidth!.Value / size.Width); } /// - /// Enum representing shape of an image + /// Resize specified Size growing/shrinking by specified % value /// - public enum ImageShape + public static Size ResizePercent(Size size, float percentage) { - /// - /// Width == Height - /// - Square = 0, - - /// - /// Width > Height - /// - Landscape = 1, - - /// - /// Width < Height - /// - Portrait = 2 + var width = Convert.ToInt32(size.Width * (percentage / 100)); + var height = Convert.ToInt32(size.Height * (percentage / 100)); + return new Size(width, height); } + + /// + /// Get % size difference between larger and smaller size, based on longest edge. + /// + public static double GetSizeIncreasePercent(Size largerSize, Size smallerSize) + { + var largeMax = largerSize.MaxDimension; + var smallMax = smallerSize.MaxDimension; + if (smallMax > largeMax) throw new InvalidOperationException("Larger size must be larger than smaller"); + + return (largeMax / (double)smallMax - 1) * 100; + } + + /// + /// Get the shape of image based on it's dimensions. + /// + public ImageShape GetShape() + { + if (Width == Height) return ImageShape.Square; + + return Width > Height ? ImageShape.Landscape : ImageShape.Portrait; + } +} + +/// +/// Enum representing shape of an image +/// +public enum ImageShape +{ + /// + /// Width == Height + /// + Square = 0, + + /// + /// Width > Height + /// + Landscape = 1, + + /// + /// Width < Height + /// + Portrait = 2 } \ No newline at end of file diff --git a/src/IIIF/IIIF/Usings.cs b/src/IIIF/IIIF/Usings.cs new file mode 100644 index 0000000..ab5ee13 --- /dev/null +++ b/src/IIIF/IIIF/Usings.cs @@ -0,0 +1 @@ +global using Newtonsoft.Json; \ No newline at end of file diff --git a/src/IIIF/IIIF/Utils/CollectionX.cs b/src/IIIF/IIIF/Utils/CollectionX.cs index 47834c5..174dce0 100644 --- a/src/IIIF/IIIF/Utils/CollectionX.cs +++ b/src/IIIF/IIIF/Utils/CollectionX.cs @@ -1,10 +1,11 @@ using System.Collections.Generic; -namespace IIIF.Utils +namespace IIIF.Utils; + +internal static class CollectionX { - internal static class CollectionX + public static bool IsNullOrEmpty(this List collection) { - public static bool IsNullOrEmpty(this List collection) - => collection == null || collection.Count == 0; + return collection == null || collection.Count == 0; } } \ No newline at end of file diff --git a/src/IIIF/IIIF/V2ServiceReference.cs b/src/IIIF/IIIF/V2ServiceReference.cs index 21901a3..fd73aea 100644 --- a/src/IIIF/IIIF/V2ServiceReference.cs +++ b/src/IIIF/IIIF/V2ServiceReference.cs @@ -1,23 +1,20 @@ -using Newtonsoft.Json; +namespace IIIF; -namespace IIIF +public class V2ServiceReference : IService { - public class V2ServiceReference : IService - { - [JsonProperty(PropertyName = "@id", Order = 1)] - public string? Id { get; set; } - - [JsonProperty(PropertyName = "@type", Order = 2)] - public string? Type { get; set; } + [JsonProperty(PropertyName = "@id", Order = 1)] + public string? Id { get; set; } - public V2ServiceReference() - { - } + [JsonProperty(PropertyName = "@type", Order = 2)] + public string? Type { get; set; } - public V2ServiceReference(IService service) - { - Id = service.Id; - Type = service.Type; - } + public V2ServiceReference() + { + } + + public V2ServiceReference(IService service) + { + Id = service.Id; + Type = service.Type; } } \ No newline at end of file